1 | ;---------------------------------------------------------------------- |
---|
2 | ; Many of the following functions and procedures were written by: |
---|
3 | ; Mohammad Abouali, maboualiedu@gmail.com |
---|
4 | ; May 22nd, July 30th, 2011 |
---|
5 | ; |
---|
6 | ; http://sites.google.com/site/maboualihome/ |
---|
7 | ; |
---|
8 | ; Many functions were updated, fixed, or added by Mary Haley. |
---|
9 | ; See notes below. |
---|
10 | ; |
---|
11 | ; A debt of gratitude is owed to the ESMF development team, and |
---|
12 | ; especially Robert Oehmke, for help in using the ESMF software. |
---|
13 | ; |
---|
14 | ; This script was added to the NCL trunk on 13 Oct 2011. |
---|
15 | ; |
---|
16 | ; Important modifications: |
---|
17 | ; 18 Dec 2013: |
---|
18 | ; Added some code to get ready for automatically setting |
---|
19 | ; SrcMask2D. |
---|
20 | ; |
---|
21 | ; 23 Oct 2013: |
---|
22 | ; Added RemovePETLog option to ESMF_regrid so user can |
---|
23 | ; ask to have the PET0.RegridWeightGen.Log file removed. |
---|
24 | ; |
---|
25 | ; 14 Aug 2013: |
---|
26 | ; Added TriangularMesh, so that user can input own |
---|
27 | ; triangular mesh to unstruct_to_ESMF code. This |
---|
28 | ; is to get around the issue of csstri being slow, |
---|
29 | ; and potentially chewing up memory (need to verify the |
---|
30 | ; memory hog claim). |
---|
31 | ; |
---|
32 | ; 25 Oct 2012: |
---|
33 | ; The LargeFile option is now True by default. |
---|
34 | ; |
---|
35 | ; 17 Oct 2012: |
---|
36 | ; Attributes attached to regridded variable were |
---|
37 | ; cleaned up. A new "remap" attribute will be attached |
---|
38 | ; to the regridded variable. |
---|
39 | ; |
---|
40 | ; 16 Oct 2012: |
---|
41 | ; ESMF_regrid and ESMF_regrid_with_weights will now |
---|
42 | ; return float if a float is input. Double is |
---|
43 | ; returned otherwise. If you still want a double |
---|
44 | ; returned, set ReturnDouble to True. |
---|
45 | ; This was changed AFTER 6.1.0-beta. |
---|
46 | ; |
---|
47 | ; 31 Jul 2012: |
---|
48 | ; Added a check_grid_type function to check for valid |
---|
49 | ; grid types ("rectilinear","5deg","G64", etc) and |
---|
50 | ; return necessary information. |
---|
51 | ; 29 Jul 2012: |
---|
52 | ; Fixed the code for copying coordinates to use the |
---|
53 | ; values on the weights file instead of the description |
---|
54 | ; file. ESMF_copy_varcoords is the new procedure. |
---|
55 | ; This change pretty much renders all of the |
---|
56 | ; retrieve_SCRIP_xxx, retrieve_ESMF_xxx, |
---|
57 | ; retrieve_dstGrid_xxx, and retrieve_srcGrid_xxx |
---|
58 | ; obsolete. |
---|
59 | ; |
---|
60 | ; ESMF_regrid_with_weights now copies attributes and |
---|
61 | ; coordinates by default. |
---|
62 | ; (CopyVarCoords and CopyVarAtts now default True.) |
---|
63 | ; |
---|
64 | ; Added new CopyVarMeta attribute, which is True by default. |
---|
65 | ; Setting this to False overrides CopyVarCoords and CopyVarAtts. |
---|
66 | ; |
---|
67 | ; 20 Jun 2012: |
---|
68 | ; Fix the section that calculates the corner grid cells |
---|
69 | ; so that the appropriate algorithm is used depending |
---|
70 | ; on whether the lat values go to -90/90. |
---|
71 | ; |
---|
72 | ; 6 May 2012: |
---|
73 | ; Clean up error statements. |
---|
74 | ; Renamed RemoveDstGridFile, RemoveSrcGridFile, and |
---|
75 | ; RemoveGridFiles to RemoveDstFile, RemoveSrcFile, |
---|
76 | ; and RemoveFiles. |
---|
77 | ; Renamed ForceOverWrite and OverWrite to |
---|
78 | ; ForceOverwrite and Overwrite (lowercase 'w'). |
---|
79 | ; Added check for duplicate options being set |
---|
80 | ; (like SrcOverwrite and Overwrite). |
---|
81 | ; Renamed DstGridFileName / SrcGridFileName to |
---|
82 | ; just DstFileName / SrcFileName. |
---|
83 | ; |
---|
84 | ; 2 May 2012: |
---|
85 | ; Changed default names of description files to |
---|
86 | ; source_grid_file.nc and |
---|
87 | ; destination_grid_file.nc |
---|
88 | ; |
---|
89 | ; 27 April 2012: |
---|
90 | ; Renamed CornerLat and CornerLon to GridCornerLat and |
---|
91 | ; GridCornerLon to make it more clear what these are. |
---|
92 | ; |
---|
93 | ; 31 Jul 2012: |
---|
94 | ; Added a check_grid_type function to check for valid |
---|
95 | ; grid types ("rectilinear","5deg","G64", etc) and |
---|
96 | ; return necessary information. |
---|
97 | ; 29 Jul 2012: |
---|
98 | ; Fixed the code for copying coordinates to use the |
---|
99 | ; values on the weights file instead of the description |
---|
100 | ; file. ESMF_copy_varcoords is the new procedure. |
---|
101 | ; This change pretty much renders all of the |
---|
102 | ; retrieve_SCRIP_xxx, retrieve_ESMF_xxx, |
---|
103 | ; retrieve_dstGrid_xxx, and retrieve_srcGrid_xxx |
---|
104 | ; obsolete. |
---|
105 | ; |
---|
106 | ; ESMF_regrid_with_weights now copies attributes and |
---|
107 | ; coordinates by default. |
---|
108 | ; (CopyVarCoords and CopyVarAtts now default True.) |
---|
109 | ; |
---|
110 | ; Added new CopyVarMeta attribute, which is True by default. |
---|
111 | ; Setting this to False overrides CopyVarCoords and CopyVarAtts. |
---|
112 | ; |
---|
113 | ; 20 Jun 2012: |
---|
114 | ; Fix the section that calculates the corner grid cells |
---|
115 | ; so that the appropriate algorithm is used depending |
---|
116 | ; on whether the lat values go to -90/90. |
---|
117 | ; |
---|
118 | ; 6 May 2012: |
---|
119 | ; Clean up error statements. |
---|
120 | ; Renamed RemoveDstGridFile, RemoveSrcGridFile, and |
---|
121 | ; RemoveGridFiles to RemoveDstFile, RemoveSrcFile, |
---|
122 | ; and RemoveFiles. |
---|
123 | ; Renamed ForceOverWrite and OverWrite to |
---|
124 | ; ForceOverwrite and Overwrite (lowercase 'w'). |
---|
125 | ; Added check for duplicate options being set |
---|
126 | ; (like SrcOverwrite and Overwrite). |
---|
127 | ; Renamed DstGridFileName / SrcGridFileName to |
---|
128 | ; just DstFileName / SrcFileName. |
---|
129 | ; |
---|
130 | ; 2 May 2012: |
---|
131 | ; Changed default names of description files to |
---|
132 | ; source_grid_file.nc and |
---|
133 | ; destination_grid_file.nc |
---|
134 | ; |
---|
135 | ; 27 April 2012: |
---|
136 | ; Renamed CornerLat and CornerLon to GridCornerLat and |
---|
137 | ; GridCornerLon to make it more clear what these are. |
---|
138 | ; |
---|
139 | ; 26 April 2012: |
---|
140 | ; For "conserve" method, be sure to divide results by |
---|
141 | ; "frac_b". This also meant I had to modify "reshape" |
---|
142 | ; so I could use it to reshape *and* conform the frac_b |
---|
143 | ; array to the destination grid size. |
---|
144 | ; |
---|
145 | ; 22 April 2012: |
---|
146 | ; Renamed "method" and "pole" attributes to "InterpMethod" |
---|
147 | ; and "Pole" for consistency with other attributes. "method" |
---|
148 | ; and "pole" will still work. |
---|
149 | ; Added RemoveGridFiles, RemoveSrcGridFile, RemoveDstGridFile |
---|
150 | ; to ESMF_regrid_gen_weights. |
---|
151 | ; |
---|
152 | ; 13 April 2012: |
---|
153 | ; Removed ForcedCorners attribute. The user |
---|
154 | ; can just set CornerLat and CornerLon. |
---|
155 | ; |
---|
156 | ; 08 March 2012: |
---|
157 | ; Renamed this script to ESMF_regridding.ncl |
---|
158 | ; |
---|
159 | ; 06 March 2012: |
---|
160 | ; Renamed ESMF_gen_weights to ESMF_regrid_gen_weights |
---|
161 | ; Renamed ESMF_regrid to ESMF_regrid_with_weights |
---|
162 | ; Renamed ESMF_all to ESMF_regrid |
---|
163 | ; |
---|
164 | ; 08 Feb 2012: |
---|
165 | ; Added retrieve_ESMF_lat and retrieve_ESMF_lon. |
---|
166 | ; Added ESMF_all |
---|
167 | ; Added write_grid_description_file. |
---|
168 | ; Removed isbothvaratt_logical_true and made isatt_logical_true |
---|
169 | ; behave like it. |
---|
170 | ; Added isatt_logical_false. |
---|
171 | ; Set IgnoreMappedPoints to True by default. |
---|
172 | ; Set pole="none" if InterpMethod="conserve" (and pole not |
---|
173 | ; explicitly set by user). |
---|
174 | ; |
---|
175 | ; 23 Jan 2012: |
---|
176 | ; Removed BothRegional and BothESMF, since they might |
---|
177 | ; confuse users. Use Src/DstRegional and Src/DstESMF. |
---|
178 | ; |
---|
179 | ; 13 Jan 2012: |
---|
180 | ; Renamed this script to ESMFRegriddingTools.ncl |
---|
181 | ; |
---|
182 | ; 12 Jan 2012: |
---|
183 | ; Moved copy_VarAtts_Except to contributed.ncl and |
---|
184 | ; renamed to copy_VarAtts_except. |
---|
185 | ; |
---|
186 | ; 10 Jan 2012: |
---|
187 | ; Overhauled latlon_to_SCRIP to accept a grid type |
---|
188 | ; as input, like "1x1", "0.25", "G64", etc. |
---|
189 | ; Overhauled rectilinear_to_SCRIP, curvilinear_to_SCRIP |
---|
190 | ; latlon_to_SCRIP, and unstructured_to_ESMF to turn some |
---|
191 | ; of the input arguments (like a title) into "opt" |
---|
192 | ; attributes. Also removed "mask2d" as an input argument, |
---|
193 | ; and made it an "opt" attribute. |
---|
194 | ; |
---|
195 | ; 09 Jan 2012: |
---|
196 | ; Renamed cartesian_to_SCRIP to latlon_to_SCRIP |
---|
197 | ; Renamed some of the attributes in this function. |
---|
198 | ; Added PrintTimings to the xxxx_to_SCRIP, xxxx_to_ESMF, |
---|
199 | ; and ESMF_gen_weights functions. |
---|
200 | ; |
---|
201 | ; 01 Jan 2012: |
---|
202 | ; Changed behavior of ForceOverWrite. No longer |
---|
203 | ; requires OverWrite to be set. Removed |
---|
204 | ; ForceUnstructured since it's not really needed. |
---|
205 | ; Added get_start_time and print_elapsed_time. |
---|
206 | ; |
---|
207 | ; 22 Dec 2011: |
---|
208 | ; Renamed some more functions: changed "esmf" to "ESMF" |
---|
209 | ; and "scrip" to "SCRIP". |
---|
210 | ; Changed esmf_remap_weights to ESMF_gen_weights. |
---|
211 | ; Changed esmf_remap to ESMF_regrid |
---|
212 | ; Converted ESMF_gen_weights to a procedure. |
---|
213 | ; Other changes to clean up error statements, remove |
---|
214 | ; extraneous print statements, add more comments. |
---|
215 | ; |
---|
216 | ; 20 Dec 2011: |
---|
217 | ; Combined GenBox_with_LLURCorner and GenBox_with_Center_Dim |
---|
218 | ; into one script called "cartesian_to_SCRIP". |
---|
219 | ; |
---|
220 | ; 18 Dec 2011: |
---|
221 | ; Cleaned up Auto2SCRIP: |
---|
222 | ; - changed to procedure |
---|
223 | ; - renamed to auto_to_SCRIP |
---|
224 | ; - changed values for "coordType" |
---|
225 | ; - cleaned up "if-else-endif" statements |
---|
226 | ; |
---|
227 | ; 18 Dec 2011: |
---|
228 | ; Cleaned up this script to: |
---|
229 | ; - rename some functions and/or change to procedures |
---|
230 | ; - use better functions where appropriate |
---|
231 | ; - align code to make it easier to read |
---|
232 | ; - add routine names to error prints |
---|
233 | ; |
---|
234 | ; 15 Dec 2011: |
---|
235 | ; Added the "-i" flag to the ESMF_RegridWeightGen |
---|
236 | ; command to properly handle regional grids like WRF. |
---|
237 | ; The WRF regridding example wasn't working otherwise. |
---|
238 | ; |
---|
239 | ; 09 Nov 2011: |
---|
240 | ; Decided to completely remove "remove outlier" code. |
---|
241 | ; Cleaned up the sparse_matrix_mult code to correctly |
---|
242 | ; handle the case of the source grid being 1D. |
---|
243 | ; |
---|
244 | ; 06 Nov 2011: |
---|
245 | ; Made code faster by adding option |
---|
246 | ; ("RemoveOutliers", False by default) to not |
---|
247 | ; remove outliers. It turns out this code was |
---|
248 | ; added to fix bug in original SMM code in |
---|
249 | ; handling missing values. This code should no |
---|
250 | ; longer be needed. |
---|
251 | ; |
---|
252 | ; Added PrintTimings to time the sparse matrix multiply |
---|
253 | ; and the removing outliers code. |
---|
254 | ; |
---|
255 | ; 02 Nov 2011: |
---|
256 | ; Replaced the SMM call with the new sparse_matrix_mult |
---|
257 | ; function. |
---|
258 | ; |
---|
259 | ; 01 Nov 2011: |
---|
260 | ; Created a built-in version of "reshape" which doesn't |
---|
261 | ; copy any metadata. Removed the "reshape" that was in |
---|
262 | ; this file. |
---|
263 | ; |
---|
264 | ; 31 Oct 2011: |
---|
265 | ; Replaced "return(tointeger(1))" with just "return(1)" |
---|
266 | ; Removed SMM and squeeze, since they don't seemed to be |
---|
267 | ; used by anybody. |
---|
268 | ; |
---|
269 | ; 14 Oct 2011: |
---|
270 | ; Removed references to $ESMFBINDIR. This should be on |
---|
271 | ; user's search path. Also, executable had wrong name, |
---|
272 | ; changed to "ESMF_RegridWeightGen". |
---|
273 | |
---|
274 | ;====================================================================== |
---|
275 | ; This function returns the start time, either in CPU or wall |
---|
276 | ; clock seconds, depending on your version of NCL. Use this function |
---|
277 | ; with print_elapsed_time, below. |
---|
278 | ;====================================================================== |
---|
279 | undef("get_start_time") |
---|
280 | function get_start_time() |
---|
281 | local s |
---|
282 | begin |
---|
283 | s = str_split(get_ncl_version(),".") |
---|
284 | if(s(0).eq."5".or.(s(0).eq."6".and.s(1).eq."0")) then |
---|
285 | return(systemfunc("date")) |
---|
286 | else |
---|
287 | return(get_cpu_time()) |
---|
288 | end if |
---|
289 | end |
---|
290 | |
---|
291 | ;====================================================================== |
---|
292 | ; This procedure prints the elapsed time, either in CPU or wall |
---|
293 | ; clock seconds, depending on your version of NCL. Use this procedure |
---|
294 | ; with get_start_time, above. |
---|
295 | ;====================================================================== |
---|
296 | undef("print_elapsed_time") |
---|
297 | procedure print_elapsed_time(stime,title) |
---|
298 | local s |
---|
299 | begin |
---|
300 | s = str_split(get_ncl_version(),".") |
---|
301 | if(s(0).eq."5".or.(s(0).eq."6".and.s(1).eq."0")) then |
---|
302 | wallClockElapseTime(stime, title, 0) |
---|
303 | else |
---|
304 | diff_time = get_cpu_time() - stime |
---|
305 | print("=====> CPU Elapsed Time: " + title + ": " + diff_time + " seconds <=====") |
---|
306 | end if |
---|
307 | end |
---|
308 | |
---|
309 | ;====================================================================== |
---|
310 | ; This function receives a set of variable names and checks |
---|
311 | ; if their units attribute contains north or east |
---|
312 | ; If it contains: |
---|
313 | ; north, it returns "latitude" |
---|
314 | ; east, it returns "longitude" |
---|
315 | ; otherwise it returns "unknown" |
---|
316 | ;====================================================================== |
---|
317 | undef("isLatorLon") |
---|
318 | function isLatorLon(fid,VarNames[*]:string) |
---|
319 | local NumNames, OutPut, i |
---|
320 | begin |
---|
321 | NumNames = dimsizes(VarNames) |
---|
322 | OutPut = new(NumNames,"string","No_FillValue") |
---|
323 | OutPut = "unknown" |
---|
324 | do i=0,NumNames-1 |
---|
325 | if (isfilevar(fid,VarNames(i)).and. \ |
---|
326 | isfilevaratt(fid,VarNames(i),"units")) then |
---|
327 | if( isStrSubset(str_lower(fid->$VarNames(i)$@units),"north") ) then |
---|
328 | OutPut(i)="latitude" |
---|
329 | else if ( isStrSubset(str_lower(fid->$VarNames(i)$@units),"east") ) then |
---|
330 | OutPut(i)="longitude" |
---|
331 | end if |
---|
332 | end if |
---|
333 | end if |
---|
334 | end do |
---|
335 | return(OutPut) |
---|
336 | end |
---|
337 | |
---|
338 | ;***********************************************************************; |
---|
339 | ; Function : get_att_value( ; |
---|
340 | ; opt[1] : logical ; |
---|
341 | ; attname : string ; |
---|
342 | ; default_val ; |
---|
343 | ; ; |
---|
344 | ; This function checks to see if the given attribute has been set, and ; |
---|
345 | ; if so, it returns its value. Otherwise, it returns the default value ; |
---|
346 | ; which is the 3rd argument. ; |
---|
347 | ;***********************************************************************; |
---|
348 | undef("get_att_value") |
---|
349 | function get_att_value(opt,attname:string,default_val) |
---|
350 | local return_val |
---|
351 | begin |
---|
352 | if(islogical(opt).and.opt.and..not.any(ismissing(getvaratts(opt))).and.\ |
---|
353 | isatt(opt,attname)) then |
---|
354 | return_val = opt@$attname$ |
---|
355 | else |
---|
356 | return_val = default_val |
---|
357 | end if |
---|
358 | return(return_val) |
---|
359 | end |
---|
360 | |
---|
361 | |
---|
362 | ;====================================================================== |
---|
363 | ; This function will return True if Opt is True and the AttName |
---|
364 | ; attribute is a logical attribute that is set to True |
---|
365 | ;====================================================================== |
---|
366 | undef("isatt_logical_true") |
---|
367 | function isatt_logical_true(Opt[1]:logical,AttName[1]:string) |
---|
368 | begin |
---|
369 | if (isatt(Opt,AttName).and.islogical(Opt@$AttName$)) then |
---|
370 | return(Opt@$AttName$) |
---|
371 | end if |
---|
372 | return(False) |
---|
373 | end ; of isatt_logical_true(...) |
---|
374 | |
---|
375 | ;====================================================================== |
---|
376 | ; This function will return True if Opt is True and the AttName |
---|
377 | ; attribute is a logical attribute that is set to False |
---|
378 | ;====================================================================== |
---|
379 | undef("isatt_logical_false") |
---|
380 | function isatt_logical_false(Opt[1]:logical,AttName[1]:string) |
---|
381 | begin |
---|
382 | if (isatt(Opt,AttName).and.islogical(Opt@$AttName$).and. \ |
---|
383 | .not.Opt@$AttName$) then |
---|
384 | return(True) |
---|
385 | end if |
---|
386 | return(False) |
---|
387 | end ; of isatt_logical_false(...) |
---|
388 | |
---|
389 | ;====================================================================== |
---|
390 | ; This procedure checks if a file already exists, and removes if |
---|
391 | ; certain options are set. |
---|
392 | ; |
---|
393 | ; If opt=True and opt@Overwrite = True, then user will be prompted |
---|
394 | ; whether to remove file. |
---|
395 | ; |
---|
396 | ; Otherwise, if opt=True and opt@ForceOverwrite = True, then file |
---|
397 | ; will be removed with no prompting. |
---|
398 | ;====================================================================== |
---|
399 | undef("check_for_file") |
---|
400 | procedure check_for_file(fname,opt) |
---|
401 | local DEBUG |
---|
402 | begin |
---|
403 | DEBUG = isatt_logical_true(opt,"Debug") |
---|
404 | if (isfilepresent(fname)) then |
---|
405 | if(isatt_logical_true(opt,"Overwrite")) then |
---|
406 | system("rm -rfi "+fname) |
---|
407 | else if (isatt_logical_true(opt,"ForceOverwrite")) then |
---|
408 | system("rm -rf "+fname) |
---|
409 | else |
---|
410 | print("check_for_file: the file '" + fname + "' already exists.") |
---|
411 | print(" Set opt@Overwrite = True") |
---|
412 | print(" or opt@ForceOverwrite = True") |
---|
413 | print(" to override.") |
---|
414 | exit |
---|
415 | end if |
---|
416 | end if |
---|
417 | end if |
---|
418 | end |
---|
419 | |
---|
420 | ;====================================================================== |
---|
421 | ; This procedure checks if two attributes are set, and set to |
---|
422 | ; different values. If so, they will be set to the given value, |
---|
423 | ; and a warning printed. |
---|
424 | ;====================================================================== |
---|
425 | undef("check_both_atts") |
---|
426 | procedure check_both_atts(opt,att1,att2,attval) |
---|
427 | begin |
---|
428 | if (isatt(opt,att1).and.isatt(opt,att2).and.\ |
---|
429 | opt@$att1$.ne.opt@$att2$) then |
---|
430 | print("check_both_atts: you have set " + att1 + " and") |
---|
431 | print(" " + att2 + " to different values.") |
---|
432 | print(" Setting them both to " + attval + " to be safe.") |
---|
433 | opt@$att1$ = attval |
---|
434 | opt@$att2$ = attval |
---|
435 | end if |
---|
436 | end |
---|
437 | |
---|
438 | ;====================================================================== |
---|
439 | ; This function coerces the type of a variable to the given type. |
---|
440 | ; It will also copy the attributes. |
---|
441 | ;====================================================================== |
---|
442 | undef("totypeof") |
---|
443 | function totypeof(inVar,outType) |
---|
444 | begin |
---|
445 | Err="OK" |
---|
446 | if (outType.eq."double") then |
---|
447 | outVar=todouble(inVar) |
---|
448 | else if (outType.eq."float") then |
---|
449 | outVar=tofloat(inVar) |
---|
450 | else if (outType.eq."integer") then |
---|
451 | outVar=tointeger(inVar) |
---|
452 | else if (outType.eq."int64") then |
---|
453 | outVar=toint64(inVar) |
---|
454 | else if (outType.eq."uint64") then |
---|
455 | outVar=touint64(inVar) |
---|
456 | else if (outType.eq."long") then |
---|
457 | outVar=tolong(inVar) |
---|
458 | else if (outType.eq."ulong") then |
---|
459 | outVar=toulong(inVar) |
---|
460 | else if (outType.eq."uint") then |
---|
461 | outVar=touint(inVar) |
---|
462 | else if (outType.eq."short") then |
---|
463 | outVar=toshort(inVar) |
---|
464 | else if (outType.eq."ushort") then |
---|
465 | outVar=toushort(inVar) |
---|
466 | else if (outType.eq."byte") then |
---|
467 | outVar=tobyte(inVar) |
---|
468 | else if (outType.eq."ubyte") then |
---|
469 | outVar=toubyte(inVar) |
---|
470 | else if (outType.eq."string") then |
---|
471 | outVar=tostring(inVar) |
---|
472 | else if (outType.eq."character") then |
---|
473 | outVar=tocharacter(inVar) |
---|
474 | else |
---|
475 | print("totypeof: Error: conversion to the provided type is not supported.") |
---|
476 | exit |
---|
477 | end if |
---|
478 | end if |
---|
479 | end if |
---|
480 | end if |
---|
481 | end if |
---|
482 | end if |
---|
483 | end if |
---|
484 | end if |
---|
485 | end if |
---|
486 | end if |
---|
487 | end if |
---|
488 | end if |
---|
489 | end if |
---|
490 | end if |
---|
491 | copy_VarAtts_except(inVar,outVar,"_FillValue") |
---|
492 | return(outVar) |
---|
493 | end ; of totypeof(...) |
---|
494 | |
---|
495 | ;====================================================================== |
---|
496 | ; This procedure rotates the given lat/lon grid, given a rotation angle. |
---|
497 | ; It operates directly on the input lat/lon. |
---|
498 | ;====================================================================== |
---|
499 | undef("rotate_latlon") |
---|
500 | procedure rotate_latlon(lat:snumeric,lon:snumeric,Rot[1]:numeric) |
---|
501 | local d2r, tmpPoints, latlon_dims |
---|
502 | begin |
---|
503 | if ( any(dimsizes(lat).ne.dimsizes(lon) ) ) then |
---|
504 | print("rotate_latlon: lat and lon must have the same dimensions.") |
---|
505 | exit |
---|
506 | end if |
---|
507 | |
---|
508 | latlon_dims = dimsizes(lat) |
---|
509 | d2r = atan(1)/45.0; |
---|
510 | RotateMat = (/(/cos(Rot*d2r), -sin(Rot*d2r)/), \ |
---|
511 | (/sin(Rot*d2r), cos(Rot*d2r)/) /) |
---|
512 | |
---|
513 | tmpPoints = new( (/2,product(latlon_dims)/),typeof(lat) ) |
---|
514 | tmpPoints(0,:) = ndtooned(lat) |
---|
515 | tmpPoints(1,:) = ndtooned(lon) |
---|
516 | tmpPoints = RotateMat#tmpPoints |
---|
517 | lat = onedtond(tmpPoints(1,:),latlon_dims) |
---|
518 | lon = onedtond(tmpPoints(0,:),latlon_dims) |
---|
519 | end |
---|
520 | |
---|
521 | ;====================================================================== |
---|
522 | ; This functions calculates the mirror of p1 with respect to po. |
---|
523 | ;====================================================================== |
---|
524 | undef("mirrorP2P") |
---|
525 | function mirrorP2P(p1,po) |
---|
526 | local dVec |
---|
527 | begin |
---|
528 | dVec = p1-po |
---|
529 | MirrorP = po-dVec |
---|
530 | return(MirrorP) |
---|
531 | end ; of mirrorP2P(...) |
---|
532 | |
---|
533 | ;====================================================================== |
---|
534 | ; This functions checks the given string for a valid grid type: |
---|
535 | ; - "rectilinear" |
---|
536 | ; - "curvilinear" |
---|
537 | ; - "unstructred" |
---|
538 | ; - "1x1", "0.25x0.25", etc. |
---|
539 | ; - "1deg", "0.25deg", etc. |
---|
540 | ; - "G64", "G128", tec. |
---|
541 | ; |
---|
542 | ; If a valid type is found, then it will be returned as a string: |
---|
543 | ; - "rectilinear" - returns "rectilinear" |
---|
544 | ; - "curvilinear" - returns "curvilinear" |
---|
545 | ; - "unstructured" - returns "unstructured" |
---|
546 | ; |
---|
547 | ; These grid types will return additional attributes: |
---|
548 | ; - "1x1", "0.25x0.25", etc - returns "deg" and nlat,nlon as attributes |
---|
549 | ; - "1deg", "0.25deg", etc - returns "deg" and nlat,nlon as attributes |
---|
550 | ; - "G64", "G128", etc - returns "gaussian" and the nlon number |
---|
551 | ; |
---|
552 | ; If a valid type is not found, then "none" is returned. |
---|
553 | ;====================================================================== |
---|
554 | undef("check_grid_type") |
---|
555 | function check_grid_type(grid_type[1]:string) |
---|
556 | local str, dlat, dlon, nlon, ret |
---|
557 | begin |
---|
558 | ret = grid_type ; this may change |
---|
559 | bad_ret = "none" |
---|
560 | if(any(grid_type.eq.(/"rectilinear","curvilinear","unstructured"/))) then |
---|
561 | return(ret) |
---|
562 | end if |
---|
563 | |
---|
564 | ;---Check for "1x1", "2x3", "0.25x0.25" format |
---|
565 | if(isStrSubset(grid_type,"x")) then |
---|
566 | str = str_split(grid_type,"x") |
---|
567 | if(dimsizes(str).ne.2) then |
---|
568 | print("check_grid_type: invalid format for NxM type of grid.") |
---|
569 | return(bad_ret) |
---|
570 | end if |
---|
571 | dlat = tofloat(str(0)) |
---|
572 | dlon = tofloat(str(1)) |
---|
573 | if(ismissing(dlat).or.ismissing(dlon)) then |
---|
574 | print("check_grid_type: invalid format for NxM type of grid.") |
---|
575 | return(bad_ret) |
---|
576 | end if |
---|
577 | ret = "degree" |
---|
578 | ret@dlat = dlat |
---|
579 | ret@dlon = dlon |
---|
580 | return(ret) |
---|
581 | end if |
---|
582 | |
---|
583 | ;---Check for "1deg", "0.25 deg" format |
---|
584 | if(isStrSubset(grid_type,"deg")) then |
---|
585 | str = str_split(grid_type,"deg") |
---|
586 | if(dimsizes(str).ne.1) then |
---|
587 | print("check_grid_type: invalid format for Ndeg type of grid.") |
---|
588 | return(bad_ret) |
---|
589 | end if |
---|
590 | dlat = tofloat(str(0)) |
---|
591 | if(ismissing(dlat)) then |
---|
592 | print("check_grid_type: invalid format for Ndeg type of grid.") |
---|
593 | return(bad_ret) |
---|
594 | end if |
---|
595 | ret = "degree" |
---|
596 | ret@dlat = dlat |
---|
597 | ret@dlon = dlat ; same value for both |
---|
598 | return(ret) |
---|
599 | end if |
---|
600 | |
---|
601 | ;---Check for "G64", "G 128" format |
---|
602 | if(isStrSubset(grid_type,"G")) then |
---|
603 | str = str_split(grid_type,"G") |
---|
604 | if(dimsizes(str).ne.1) then |
---|
605 | print("check_grid_type: invalid format for gaussian grid.") |
---|
606 | return(bad_ret) |
---|
607 | end if |
---|
608 | nlon = tointeger(str(0)) |
---|
609 | if(ismissing(nlon)) then |
---|
610 | print("check_grid_type: invalid format for gaussian grid.") |
---|
611 | return(bad_ret) |
---|
612 | end if |
---|
613 | if((nlon%2).ne.0) then |
---|
614 | print("check_grid_type: invalid number for gaussian grid.") |
---|
615 | return(bad_ret) |
---|
616 | end if |
---|
617 | ret = "gaussian" |
---|
618 | ret@nlon = nlon |
---|
619 | ret@nlat = nlon/2 |
---|
620 | return(ret) |
---|
621 | end if |
---|
622 | print("check_grid_type: invalid grid type") |
---|
623 | return(bad_ret) |
---|
624 | end |
---|
625 | |
---|
626 | ;====================================================================== |
---|
627 | ; This procedure is meant as a replacement for |
---|
628 | ; "calc_SCRIP_corners_noboundaries" (previously called |
---|
629 | ; "find_SCRIP_corners"). It takes the same arguments, but uses a |
---|
630 | ; different algorithm that was suggested by Bob Oehmke. |
---|
631 | ; |
---|
632 | ; After 6.1.0-beta, I realized that this algorithm really should only |
---|
633 | ; be used if any of the lat points are at the boundary. At this point, |
---|
634 | ; I decided to rename this function from "calc_SCRIP_corners" to |
---|
635 | ; "calc_SCRIP_corners_boundaries", and go back to using |
---|
636 | ; "calc_SCRIP_corners_noboundaries" as the regular algorithm. |
---|
637 | ;====================================================================== |
---|
638 | undef("calc_SCRIP_corners_boundaries") |
---|
639 | procedure calc_SCRIP_corners_boundaries(lat2d[*][*],lon2d[*][*], \ |
---|
640 | grid_corner_lat, grid_corner_lon,\ |
---|
641 | opt) |
---|
642 | local DEBUG, latlon_dims, grid_corner_lat2d, grid_corner_lon2d, \ |
---|
643 | nlat, nlon, nlatp1, nlonp1 |
---|
644 | begin |
---|
645 | DEBUG = isatt_logical_true(opt,"Debug") |
---|
646 | |
---|
647 | ;---Check input dimension sizes. |
---|
648 | if (any(dimsizes(lat2d).ne.dimsizes(lon2d))) then |
---|
649 | print("calc_SCRIP_corners_boundaries: lat and lon must have the same dimensions.") |
---|
650 | exit |
---|
651 | end if |
---|
652 | |
---|
653 | if (any(dimsizes(grid_corner_lat).ne.dimsizes(grid_corner_lon))) then |
---|
654 | print("calc_SCRIP_corners_boundaries: lat and lon must have the same dimensions.") |
---|
655 | exit |
---|
656 | end if |
---|
657 | |
---|
658 | ;---Get dimension sizes |
---|
659 | latlon_dims = dimsizes(lat2d) |
---|
660 | cnr_latlon_dims = dimsizes(grid_corner_lat) |
---|
661 | cnr_rank = dimsizes(cnr_latlon_dims) |
---|
662 | nlat = latlon_dims(0) |
---|
663 | nlon = latlon_dims(1) |
---|
664 | nlatp1 = nlat+1 |
---|
665 | nlonp1 = nlon+1 |
---|
666 | |
---|
667 | if(cnr_rank.ne.2.or.cnr_latlon_dims(0).ne.(nlat*nlon).or. \ |
---|
668 | cnr_latlon_dims(1).ne.4) then |
---|
669 | print("calc_SCRIP_corners_boundaries: grid_corner_lat/grid_corner_lon must be (nlat*nlon) x 4") |
---|
670 | exit |
---|
671 | end if |
---|
672 | |
---|
673 | if(DEBUG) then |
---|
674 | print("calc_SCRIP_corners_boundaries") |
---|
675 | print(" min/max original lat: " + min(lat2d) + "/" + max(lat2d)) |
---|
676 | print(" min/max original lon: " + min(lon2d) + "/" + max(lon2d)) |
---|
677 | end if |
---|
678 | |
---|
679 | ;---Create array to hold a 2D array of lat/lon corners |
---|
680 | grid_corner_lat2d = new((/nlatp1,nlonp1/),typeof(lat2d)) |
---|
681 | grid_corner_lon2d = new((/nlatp1,nlonp1/),typeof(lon2d)) |
---|
682 | |
---|
683 | ;---Calculate interior locations of corner grid |
---|
684 | do i=1,nlat-1 |
---|
685 | do j=1,nlon-1 |
---|
686 | grid_corner_lat2d(i,j) = avg((/lat2d(i-1,j-1),lat2d(i,j-1),\ |
---|
687 | lat2d(i-1,j), lat2d(i,j)/)) |
---|
688 | grid_corner_lon2d(i,j) = avg((/lon2d(i-1,j-1),lon2d(i,j-1),\ |
---|
689 | lon2d(i-1,j), lon2d(i,j)/)) |
---|
690 | end do |
---|
691 | end do |
---|
692 | |
---|
693 | ;---Calculate bottom locations of corner grid |
---|
694 | do j=1,nlon-1 |
---|
695 | grid_corner_lat2d(0,j) = avg((/lat2d(0,j),lat2d(0,j-1)/)) |
---|
696 | grid_corner_lon2d(0,j) = avg((/lon2d(0,j),lon2d(0,j-1)/)) |
---|
697 | end do |
---|
698 | |
---|
699 | ;---Calculate top locations of corner grid |
---|
700 | do j=1,nlon-1 |
---|
701 | grid_corner_lat2d(nlat,j) = avg((/lat2d(nlat-1,j), \ |
---|
702 | lat2d(nlat-1,j-1)/)) |
---|
703 | grid_corner_lon2d(nlat,j) = avg((/lon2d(nlat-1,j), \ |
---|
704 | lon2d(nlat-1,j-1)/)) |
---|
705 | end do |
---|
706 | |
---|
707 | ;---Calculate left locations of corner grid |
---|
708 | do i=1,nlat-1 |
---|
709 | grid_corner_lat2d(i,0) = avg((/lat2d(i,0),lat2d(i-1,0)/)) |
---|
710 | grid_corner_lon2d(i,0) = avg((/lon2d(i,0),lon2d(i-1,0)/)) |
---|
711 | end do |
---|
712 | |
---|
713 | ;---Calculate right locations of corner grid |
---|
714 | do i=1,nlat-1 |
---|
715 | grid_corner_lat2d(i,nlon) = avg((/lat2d(i,nlon-1), \ |
---|
716 | lat2d(i-1,nlon-1)/)) |
---|
717 | grid_corner_lon2d(i,nlon) = avg((/lon2d(i,nlon-1), \ |
---|
718 | lon2d(i-1,nlon-1)/)) |
---|
719 | end do |
---|
720 | |
---|
721 | ;---Set four corners of corner grid |
---|
722 | grid_corner_lat2d(0,0) = lat2d(0,0) |
---|
723 | grid_corner_lon2d(0,0) = lon2d(0,0) |
---|
724 | grid_corner_lat2d(0,nlon) = lat2d(0,nlon-1) |
---|
725 | grid_corner_lon2d(0,nlon) = lon2d(0,nlon-1) |
---|
726 | grid_corner_lat2d(nlat,nlon) = lat2d(nlat-1,nlon-1) |
---|
727 | grid_corner_lon2d(nlat,nlon) = lon2d(nlat-1,nlon-1) |
---|
728 | grid_corner_lat2d(nlat,0) = lat2d(nlat-1,0) |
---|
729 | grid_corner_lon2d(nlat,0) = lon2d(nlat-1,0) |
---|
730 | |
---|
731 | if(DEBUG) then |
---|
732 | print("calc_SCRIP_corners_boundaries") |
---|
733 | print(" min/max grid_corner_lat2d: " + \ |
---|
734 | min(grid_corner_lat2d) + "/" + max(grid_corner_lat2d)) |
---|
735 | print(" min/max grid_corner_lon2d: " + \ |
---|
736 | min(grid_corner_lon2d) + "/" + max(grid_corner_lon2d)) |
---|
737 | end if |
---|
738 | |
---|
739 | n = 0 |
---|
740 | do i=0,nlat-1 |
---|
741 | do j=0,nlon-1 |
---|
742 | grid_corner_lat(n,0) = grid_corner_lat2d(i, j) |
---|
743 | grid_corner_lat(n,1) = grid_corner_lat2d(i, j+1) |
---|
744 | grid_corner_lat(n,2) = grid_corner_lat2d(i+1,j+1) |
---|
745 | grid_corner_lat(n,3) = grid_corner_lat2d(i+1,j) |
---|
746 | grid_corner_lon(n,0) = grid_corner_lon2d(i, j) |
---|
747 | grid_corner_lon(n,1) = grid_corner_lon2d(i, j+1) |
---|
748 | grid_corner_lon(n,2) = grid_corner_lon2d(i+1,j+1) |
---|
749 | grid_corner_lon(n,3) = grid_corner_lon2d(i+1,j) |
---|
750 | n=n+1 |
---|
751 | end do |
---|
752 | end do |
---|
753 | |
---|
754 | if(DEBUG) then |
---|
755 | print("calc_SCRIP_corners_boundaries") |
---|
756 | print(" min/max grid_corner_lat: " + min(grid_corner_lat) + \ |
---|
757 | "/" + max(grid_corner_lat)) |
---|
758 | print(" min/max grid_corner_lon: " + min(grid_corner_lon) + \ |
---|
759 | "/" + max(grid_corner_lon)) |
---|
760 | end if |
---|
761 | |
---|
762 | end ; of calc_SCRIP_corners_boundaries(...) |
---|
763 | |
---|
764 | ;====================================================================== |
---|
765 | ; This procedure is used within the curvilinear_to_SCRIP function. |
---|
766 | ; It used to be called "find_SCRIP_corners". |
---|
767 | ; |
---|
768 | ; Given a structured grid, it looks for the four corners surrounding |
---|
769 | ; each node. It returns the center of the cells for the corners. |
---|
770 | ; |
---|
771 | ; You have to be careful with this routine. It doesn't check if you |
---|
772 | ; are at or near the edge of the globe (i.e. lat=90, or lon=-180), |
---|
773 | ; and hence it might give you corners that fall outside -90/90 for lat |
---|
774 | ; and/or 0/360 for lon. |
---|
775 | ; |
---|
776 | ; Really, if the user has a special grid, he/she should input the |
---|
777 | ; corners via the GridCornerLat and GridCornerLon resources. |
---|
778 | ;====================================================================== |
---|
779 | undef("calc_SCRIP_corners_noboundaries") |
---|
780 | procedure calc_SCRIP_corners_noboundaries(lat2d[*][*],lon2d[*][*], \ |
---|
781 | grid_corner_lat,grid_corner_lon,opt) |
---|
782 | local latlon_dims, nlat, nlon, Extlon2d, Extlat2d, ExtGridCenter_lat, \ |
---|
783 | ExtGridCenter_lon, tmp, ii, jj, DEBUG |
---|
784 | begin |
---|
785 | DEBUG = isatt_logical_true(opt,"Debug") |
---|
786 | delta = get_att_value(opt,"GridCornerDelta",0.25) |
---|
787 | |
---|
788 | if ( any(dimsizes(lat2d).ne.dimsizes(lon2d) ) ) then |
---|
789 | print("calc_SCRIP_corners_noboundaries: lat and lon must have the same dimensions.") |
---|
790 | exit |
---|
791 | end if |
---|
792 | |
---|
793 | latlon_dims = dimsizes(lat2d) |
---|
794 | nlat = latlon_dims(0) |
---|
795 | nlon = latlon_dims(1) |
---|
796 | nlatp1 = nlat+1 |
---|
797 | nlonp1 = nlon+1 |
---|
798 | nlatp2 = nlat+2 |
---|
799 | nlonp2 = nlon+2 |
---|
800 | if(DEBUG) then |
---|
801 | print("calc_SCRIP_corners_noboundaries") |
---|
802 | print(" min/max original lat: " + min(lat2d) + "/" + max(lat2d)) |
---|
803 | print(" min/max original lon: " + min(lon2d) + "/" + max(lon2d)) |
---|
804 | end if |
---|
805 | ; |
---|
806 | ; Extend the lat/lon grid (needed to calculate the |
---|
807 | ; corners at the boundaries). |
---|
808 | ; |
---|
809 | Extlat2d = new( (/nlatp2, nlonp2/),typeof(lat2d)) |
---|
810 | Extlon2d = new( (/nlatp2, nlonp2/),typeof(lon2d)) |
---|
811 | |
---|
812 | ;---The middle grid is exactly the original lat2d/lon2d arrays |
---|
813 | Extlat2d(1:nlat,1:nlon) = (/lat2d/) |
---|
814 | Extlon2d(1:nlat,1:nlon) = (/lon2d/) |
---|
815 | |
---|
816 | ;---bottom row, minus corners |
---|
817 | Extlat2d(0,1:nlon) = mirrorP2P(lat2d(1,:),lat2d(0,:)) |
---|
818 | Extlon2d(0,1:nlon) = mirrorP2P(lon2d(1,:),lon2d(0,:)) |
---|
819 | |
---|
820 | ;---top, minus corners |
---|
821 | Extlat2d(nlatp1,1:nlon) = mirrorP2P(lat2d(nlat-2,:),lat2d(nlat-1,:)) |
---|
822 | Extlon2d(nlatp1,1:nlon) = mirrorP2P(lon2d(nlat-2,:),lon2d(nlat-1,:)) |
---|
823 | |
---|
824 | ;---left, minus corners |
---|
825 | Extlat2d(1:nlat,0) = mirrorP2P(lat2d(:,1),lat2d(:,0)) |
---|
826 | Extlon2d(1:nlat,0) = mirrorP2P(lon2d(:,1),lon2d(:,0)) |
---|
827 | |
---|
828 | ;---right, minus corners |
---|
829 | Extlat2d(1:nlat,nlonp1) = mirrorP2P(lat2d(:,nlon-2),lat2d(:,nlon-1)) |
---|
830 | Extlon2d(1:nlat,nlonp1) = mirrorP2P(lon2d(:,nlon-2),lon2d(:,nlon-1)) |
---|
831 | |
---|
832 | ;---lower left corner |
---|
833 | Extlat2d(0,0) = mirrorP2P(lat2d(1,1),lat2d(0,0)) |
---|
834 | Extlon2d(0,0) = mirrorP2P(lon2d(1,1),lon2d(0,0)) |
---|
835 | |
---|
836 | ;---upper right corner |
---|
837 | Extlat2d(nlatp1,nlonp1) = mirrorP2P(lat2d(nlat-2,nlon-2), \ |
---|
838 | lat2d(nlat-1,nlon-1)) |
---|
839 | Extlon2d(nlatp1,nlonp1) = mirrorP2P(lon2d(nlat-2,nlon-2),\ |
---|
840 | lon2d(nlat-1,nlon-1)) |
---|
841 | ;---lower right corner |
---|
842 | Extlat2d(0,nlonp1) = mirrorP2P(lat2d(1,nlon-2),lat2d(0,nlon-1)) |
---|
843 | Extlon2d(0,nlonp1) = mirrorP2P(lon2d(1,nlon-2),lon2d(0,nlon-1)) |
---|
844 | |
---|
845 | ;---upper left corner |
---|
846 | Extlat2d(nlatp1,0) = mirrorP2P(lat2d(nlat-2,1),lat2d(nlat-1,0)) |
---|
847 | Extlon2d(nlatp1,0) = mirrorP2P(lon2d(nlat-2,1),lon2d(nlat-1,0)) |
---|
848 | |
---|
849 | ; |
---|
850 | ; This kludge is commented out for now, because it's not a good one. |
---|
851 | ; We need a better way to fix the boundary corners if they go over. |
---|
852 | ; |
---|
853 | ;; if(DEBUG) then |
---|
854 | ;; print("calc_SCRIP_corners_noboundaries") |
---|
855 | ;; print(" Before kludge to fix lat/lon corners...") |
---|
856 | ;; print(" min/max Extlat2d: " + min(Extlat2d) + "/" + max(Extlat2d)) |
---|
857 | ;; print(" min/max Extlon2d: " + min(Extlon2d) + "/" + max(Extlon2d)) |
---|
858 | ;; end if |
---|
859 | ;; |
---|
860 | ;;;---Kludge to cap the latitudes at -90/90 |
---|
861 | ;; Extlat2d = where(Extlat2d.lt. -90, -90,Extlat2d) |
---|
862 | ;; Extlat2d = where(Extlat2d.gt. 90, 90,Extlat2d) |
---|
863 | ;; Extlon2d = where(Extlon2d.lt. 0, 0,Extlon2d) |
---|
864 | ;; Extlon2d = where(Extlon2d.gt. 360, 360,Extlon2d) |
---|
865 | ;; |
---|
866 | ;; if(DEBUG) then |
---|
867 | ;; print("calc_SCRIP_corners_noboundaries") |
---|
868 | ;; print(" After kludge to fix lat/lon corners...") |
---|
869 | ;; print(" min/max Extlat2d: " + min(Extlat2d) + "/" + max(Extlat2d)) |
---|
870 | ;; print(" min/max Extlon2d: " + min(Extlon2d) + "/" + max(Extlon2d)) |
---|
871 | ;; end if |
---|
872 | |
---|
873 | if(DEBUG) then |
---|
874 | print("calc_SCRIP_corners_noboundaries") |
---|
875 | print(" min/max Extlat2d: " + min(Extlat2d) + "/" + max(Extlat2d)) |
---|
876 | print(" min/max Extlon2d: " + min(Extlon2d) + "/" + max(Extlon2d)) |
---|
877 | end if |
---|
878 | ; |
---|
879 | ; Calculate the cell center of the extended grid, which |
---|
880 | ; would be the corner coordinates for the original grid. |
---|
881 | ; |
---|
882 | tmp = Extlat2d(:,1:nlonp1)+Extlat2d(:,0:nlon) |
---|
883 | ExtGridCenter_lat = ndtooned((tmp(1:nlatp1,:)+tmp(0:nlat,:))*delta) |
---|
884 | |
---|
885 | tmp = Extlon2d(:,1:nlonp1)+Extlon2d(:,0:nlon) |
---|
886 | ExtGridCenter_lon = ndtooned((tmp(1:nlatp1,:)+tmp(0:nlat,:))*delta) |
---|
887 | delete(tmp) |
---|
888 | |
---|
889 | if(DEBUG) then |
---|
890 | print("calc_SCRIP_corners_noboundaries") |
---|
891 | print(" min/max ExtGridCenter_lat: " + \ |
---|
892 | min(ExtGridCenter_lat) + "/" + max(ExtGridCenter_lat)) |
---|
893 | print(" min/max ExtGridCenter_lon: " + \ |
---|
894 | min(ExtGridCenter_lon) + "/" + max(ExtGridCenter_lon)) |
---|
895 | end if |
---|
896 | ;---Extract the grid cell corners |
---|
897 | ii = ndtooned(conform_dims((/nlat,nlon/),ispan(0,nlon-1,1),1)) |
---|
898 | jj = ndtooned(conform_dims((/nlat,nlon/),ispan(0,nlat-1,1),0)) |
---|
899 | |
---|
900 | grid_corner_lat(:,0) = ExtGridCenter_lat(jj*(nlonp1)+ii) |
---|
901 | grid_corner_lat(:,1) = ExtGridCenter_lat(jj*(nlonp1)+(ii+1)) |
---|
902 | grid_corner_lat(:,2) = ExtGridCenter_lat((jj+1)*(nlonp1)+(ii+1)) |
---|
903 | grid_corner_lat(:,3) = ExtGridCenter_lat((jj+1)*(nlonp1)+ii) |
---|
904 | |
---|
905 | grid_corner_lon(:,0) = ExtGridCenter_lon(jj*(nlonp1)+ii) |
---|
906 | grid_corner_lon(:,1) = ExtGridCenter_lon(jj*(nlonp1)+(ii+1)) |
---|
907 | grid_corner_lon(:,2) = ExtGridCenter_lon((jj+1)*(nlonp1)+(ii+1)) |
---|
908 | grid_corner_lon(:,3) = ExtGridCenter_lon((jj+1)*(nlonp1)+ii) |
---|
909 | |
---|
910 | end ; of calc_SCRIP_corners_noboundaries(...) |
---|
911 | |
---|
912 | ;====================================================================== |
---|
913 | ; This procedure generates an ESMF file for an unstructured grid. |
---|
914 | ; It is sometimes better to even store logically rectangular and |
---|
915 | ; structured grids as unstructured grids. (Refer to TriPolar - TGrid) |
---|
916 | ;====================================================================== |
---|
917 | undef("unstructured_to_ESMF") |
---|
918 | procedure unstructured_to_ESMF(FName[1]:string,inLat,inLon,Opt[1]:logical) |
---|
919 | local DEBUG, lat, lon, NodeMask, ElementVertices, title |
---|
920 | begin |
---|
921 | ;---Check for options |
---|
922 | PrintTimings = isatt_logical_true(Opt,"PrintTimings") |
---|
923 | if(PrintTimings) then |
---|
924 | start_time = get_start_time() |
---|
925 | end if |
---|
926 | |
---|
927 | DEBUG = isatt_logical_true(Opt,"Debug") |
---|
928 | |
---|
929 | ;---Check if the file already exists |
---|
930 | check_for_file(FName,Opt) |
---|
931 | |
---|
932 | ;---Do we need to create a large file? |
---|
933 | if (.not.isatt_logical_false(Opt,"LargeFile")) then |
---|
934 | setfileoption("nc","Format","LargeFile") |
---|
935 | end if |
---|
936 | |
---|
937 | lat = ndtooned(inLat) |
---|
938 | lon = ndtooned(inLon) |
---|
939 | |
---|
940 | if (dimsizes(lat).ne.dimsizes(lon)) then |
---|
941 | print("unstructured_to_ESMF: latitude and longitude must have the same number of elements.") |
---|
942 | exit |
---|
943 | end if |
---|
944 | ;---Was a mask provided? |
---|
945 | if(Opt.and.isatt(Opt,"Mask2D")) then |
---|
946 | if(.not.all(product(dimsizes(Opt@Mask2D)).eq.dimsizes(lat))) then |
---|
947 | print("unstructured_to_ESMF: Opt@Mask2D is not the correct dimensionality") |
---|
948 | exit |
---|
949 | else |
---|
950 | NodeMask = ndtooned(Opt@Mask2D) |
---|
951 | end if |
---|
952 | else |
---|
953 | ;---No masking |
---|
954 | NodeMask = new(dimsizes(lat), "integer","No_FillValue") |
---|
955 | NodeMask = 1 |
---|
956 | end if |
---|
957 | |
---|
958 | if(Opt.and.isatt(Opt,"InputFileName")) then |
---|
959 | inputFName = Opt@InputFileName |
---|
960 | else |
---|
961 | inputFName = "none provided" |
---|
962 | end if |
---|
963 | |
---|
964 | ;---Was a triangular mesh provided? |
---|
965 | if(Opt.and.isatt(Opt,"TriangularMesh")) then |
---|
966 | dims = dimsizes(Opt@TriangularMesh) |
---|
967 | if(dims(1).ne.3) then |
---|
968 | print("unstructured_to_ESMF: TriangularMesh must be an n x 3 array") |
---|
969 | exit |
---|
970 | else |
---|
971 | if(DEBUG) then |
---|
972 | print("unstructured_to_ESMF: using triangular mesh provided by the user") |
---|
973 | end if |
---|
974 | ElementVertices = Opt@TriangularMesh |
---|
975 | end if |
---|
976 | ;---Was a quadrilateral mesh provided? |
---|
977 | else if(Opt.and.isatt(Opt,"QuadrilateralMesh")) then |
---|
978 | dims = dimsizes(Opt@QuadrilateralMesh) |
---|
979 | if(dims(1).ne.4) then |
---|
980 | print("unstructured_to_ESMF: QuadrilateralMesh must be an n x 4 array") |
---|
981 | exit |
---|
982 | else |
---|
983 | if(DEBUG) then |
---|
984 | print("unstructured_to_ESMF: using quadrilateral mesh provided by the user") |
---|
985 | end if |
---|
986 | ElementVertices = Opt@QuadrilateralMesh |
---|
987 | end if |
---|
988 | else |
---|
989 | ;---Create a triangular mesh if no mesh provided |
---|
990 | if(DEBUG) then |
---|
991 | print("unstructured_to_ESMF: triangulating the data ... this can be slow") |
---|
992 | end if |
---|
993 | ElementVertices = csstri(lat,lon) ; Triangulate |
---|
994 | end if |
---|
995 | end if |
---|
996 | if(DEBUG) then |
---|
997 | print("min/max ElementVertices = " + min(ElementVertices) + "/" + \ |
---|
998 | max(ElementVertices)) |
---|
999 | end if |
---|
1000 | ElementVertices_Dim = dimsizes(ElementVertices) |
---|
1001 | |
---|
1002 | if(DEBUG) then |
---|
1003 | print("unstructured_to_ESMF: total number of elements created: "+ \ |
---|
1004 | ElementVertices_Dim(0)) |
---|
1005 | end if |
---|
1006 | |
---|
1007 | ;---Create the file |
---|
1008 | fid = addfile(FName,"c") |
---|
1009 | setfileoption(fid,"DefineMode",True) |
---|
1010 | |
---|
1011 | ;---Define the file attributes |
---|
1012 | FileAtt = True |
---|
1013 | FileAtt@gridType = "unstructured" |
---|
1014 | FileAtt@Conventions = "ESMF" |
---|
1015 | FileAtt@Createdby = "ESMF_regridding.ncl" |
---|
1016 | FileAtt@version = "0.9" |
---|
1017 | FileAtt@inputFile = inputFName |
---|
1018 | FileAtt@timeGenerated = systemfunc("date") |
---|
1019 | FileAtt@date_created = FileAtt@timeGenerated |
---|
1020 | fileattdef(fid,FileAtt) |
---|
1021 | |
---|
1022 | ;---Define the ESMF dimensions |
---|
1023 | nodeCount = dimsizes(lat) |
---|
1024 | elementCount = ElementVertices_Dim(0) |
---|
1025 | maxNodePElement = ElementVertices_Dim(1) |
---|
1026 | coordDim = 2 |
---|
1027 | ; 0 1 2 3 |
---|
1028 | FDimNames=(/ "nodeCount","elementCount","maxNodePElement","coordDim" /) |
---|
1029 | FDimSizes=(/ nodeCount , elementCount , maxNodePElement , coordDim /) |
---|
1030 | FDimUnlim=(/ False , False , False , False /) |
---|
1031 | |
---|
1032 | filedimdef(fid,FDimNames,FDimSizes,FDimUnlim) |
---|
1033 | |
---|
1034 | ;---Define variables |
---|
1035 | filevardef(fid,"nodeCoords", "double", (/ FDimNames(0), FDimNames(3) /)) |
---|
1036 | filevardef(fid,"elementConn", "integer",(/ FDimNames(1), FDimNames(2) /)) |
---|
1037 | filevardef(fid,"numElementConn","byte", (/ FDimNames(1) /)) |
---|
1038 | filevardef(fid,"centerCoords", "double", (/ FDimNames(1), FDimNames(3) /)) |
---|
1039 | filevardef(fid,"elementArea", "double", (/ FDimNames(1) /)) |
---|
1040 | filevardef(fid,"elementMask", "integer",(/ FDimNames(1) /)) |
---|
1041 | |
---|
1042 | ;---Define the variables unit attribute |
---|
1043 | AttNodeCoords = 0 |
---|
1044 | AttNodeCoords@units = "degrees" |
---|
1045 | |
---|
1046 | AttElementConn = 0 |
---|
1047 | AttElementConn@long_name = "Node Indices that define the element connectivity" |
---|
1048 | AttElementConn@_FillValue = -1 |
---|
1049 | |
---|
1050 | AttNumElementConn = 0 |
---|
1051 | AttNumElementConn@long_name = "Number of nodes per element" |
---|
1052 | |
---|
1053 | AttCenterCoords = 0 |
---|
1054 | AttCenterCoords@units = "degrees" |
---|
1055 | |
---|
1056 | AttElementArea = 0 |
---|
1057 | AttElementArea@units = "radians^2" |
---|
1058 | AttElementArea@long_name = "area weights" |
---|
1059 | |
---|
1060 | filevarattdef(fid,"nodeCoords", AttNodeCoords) |
---|
1061 | filevarattdef(fid,"elementConn", AttElementConn) |
---|
1062 | filevarattdef(fid,"numElementConn",AttNumElementConn) |
---|
1063 | filevarattdef(fid,"centerCoords", AttCenterCoords) |
---|
1064 | filevarattdef(fid,"elementArea", AttElementArea) |
---|
1065 | |
---|
1066 | ;---Prepare the file to store the values |
---|
1067 | setfileoption(fid,"DefineMode",False) |
---|
1068 | |
---|
1069 | ;---Store Node Coordinates |
---|
1070 | nodeCoords = new((/nodeCount,coordDim/),"double","No_FillValue") |
---|
1071 | nodeCoords(:,0) = lon |
---|
1072 | nodeCoords(:,1) = lat |
---|
1073 | fid->nodeCoords = (/nodeCoords/) |
---|
1074 | |
---|
1075 | ;---Store Element Connectivity |
---|
1076 | fid->elementConn = (/ElementVertices+1/) |
---|
1077 | |
---|
1078 | ;---Store numElementConn |
---|
1079 | numElementConn = new((/elementCount/),"byte","No_FillValue") |
---|
1080 | numElementConn = tobyte(3) |
---|
1081 | fid->numElementConn = (/numElementConn/) |
---|
1082 | |
---|
1083 | ;---Store center of each element, but first calculating the centeroids. |
---|
1084 | centerCoords = new((/elementCount,coordDim/),"double","No_FillValue") |
---|
1085 | centerCoords(:,0) = dim_avg(reshape(lon(ndtooned(ElementVertices)), \ |
---|
1086 | (/elementCount,3/))) |
---|
1087 | centerCoords(:,1) = dim_avg(reshape(lat(ndtooned(ElementVertices)), \ |
---|
1088 | (/elementCount,3/))) |
---|
1089 | fid->centerCoords = (/centerCoords/) |
---|
1090 | |
---|
1091 | ;---Store element area |
---|
1092 | d2r = atan(1)/45.0; |
---|
1093 | ;; elementArea = new(elementCount,"double","No_FillValue") |
---|
1094 | ;; Redundant? Maybe needed to remove _FillValue? |
---|
1095 | ;; elementalLat = new((/elementCount,3/),"double","No_FillValue") |
---|
1096 | ;; elementalLon = new((/elementCount,3/),"double","No_FillValue") |
---|
1097 | elementalLat = todouble(reshape(lat(ndtooned(ElementVertices)),(/elementCount,3/))) |
---|
1098 | elementalLon = todouble(reshape(lon(ndtooned(ElementVertices)),(/elementCount,3/))) |
---|
1099 | elementArea = todouble(gc_tarea(elementalLat,elementalLon)) |
---|
1100 | delete(elementalLat) |
---|
1101 | delete(elementalLon) |
---|
1102 | if(DEBUG) then |
---|
1103 | print("unstructured_to_ESMF: Element Area: min:" + min(elementArea) + \ |
---|
1104 | " max:" + max(elementArea)) |
---|
1105 | end if |
---|
1106 | fid->elementArea = (/elementArea/) |
---|
1107 | |
---|
1108 | ;---Store the element Mask |
---|
1109 | elementMask = dim_sum(reshape(NodeMask(ndtooned(ElementVertices)), \ |
---|
1110 | (/elementCount,3/))) |
---|
1111 | elementMask = where(elementMask.eq.3,1,0) |
---|
1112 | fid->elementMask = (/elementMask/) |
---|
1113 | |
---|
1114 | if(PrintTimings) then |
---|
1115 | print_elapsed_time(start_time,"unstructured_to_ESMF") |
---|
1116 | end if |
---|
1117 | end ; of unstructured_to_ESMF(...) |
---|
1118 | ;====================================================================== |
---|
1119 | ; This function receives a 2D curvilinear grid and mask and stores |
---|
1120 | ; it in FName NetCDF file based on SCRIP standard. |
---|
1121 | ; lat2d and lon2d must have (nlat,nlot) dimension. |
---|
1122 | ; Opt controls the behavior of the function |
---|
1123 | ; current attribute in Opt are: |
---|
1124 | ; (1) Overwrite [Logical] if True, and if the out file already exists |
---|
1125 | ; it will erase the file. |
---|
1126 | ; (2) ForceOverwrite [Logical] If set to True, the user is not asked |
---|
1127 | ; for removing an existing file. If set to false the user permission |
---|
1128 | ; is required to remove an existing file. This is ineffective if |
---|
1129 | ; Overwrite is set to False. |
---|
1130 | ; (3) If GridCornerLat and GridCornerLon are set, then these will be used |
---|
1131 | ; instead of calcuation the corner points for the lat/lon cells. |
---|
1132 | ; The corner points are needed for the "conserve" method. |
---|
1133 | ;====================================================================== |
---|
1134 | undef("curvilinear_to_SCRIP") |
---|
1135 | procedure curvilinear_to_SCRIP(FName[1]:string,lat2d[*][*]:numeric,\ |
---|
1136 | lon2d[*][*]:numeric,Opt[1]:logical) |
---|
1137 | local latlon_dims, nlat, nlon, fid, FileAtt, grid_siz, grid_corners, \ |
---|
1138 | grid_rank, FDimNames, FDimSizes, FDimUnlim, DummyAtt1, DummyAtt2, \ |
---|
1139 | GridCornerLat, GridCornerLon, grid_corner_lat, grid_corner_lon, mask2d, \ |
---|
1140 | lat_type, lon_type, DEBUG |
---|
1141 | begin |
---|
1142 | ;---Check for options |
---|
1143 | PrintTimings = isatt_logical_true(Opt,"PrintTimings") |
---|
1144 | if(PrintTimings) then |
---|
1145 | start_time = get_start_time() |
---|
1146 | end if |
---|
1147 | |
---|
1148 | DEBUG = isatt_logical_true(Opt,"Debug") |
---|
1149 | |
---|
1150 | ;---Check if the file already exists |
---|
1151 | check_for_file(FName,Opt) |
---|
1152 | |
---|
1153 | ;---Do we need to create a large file? |
---|
1154 | if (.not.isatt_logical_false(Opt,"LargeFile")) then |
---|
1155 | setfileoption("nc","Format","LargeFile") |
---|
1156 | end if |
---|
1157 | |
---|
1158 | if ( any(dimsizes(lat2d).ne.dimsizes(lon2d)) ) then |
---|
1159 | print("curvilinear_to_SCRIP: latitude and longitude must have the same number of elements.") |
---|
1160 | exit |
---|
1161 | end if |
---|
1162 | |
---|
1163 | latlon_dims = dimsizes(lat2d) |
---|
1164 | nlat = latlon_dims(0) |
---|
1165 | nlon = latlon_dims(1) |
---|
1166 | |
---|
1167 | if(Opt.and.isatt(Opt,"Title")) then |
---|
1168 | FTitle = Opt@Title |
---|
1169 | else |
---|
1170 | FTitle = "curvilinear_to_SCRIP (" + nlat + "," + nlon + ")" |
---|
1171 | end if |
---|
1172 | |
---|
1173 | ;---Was a mask provided? |
---|
1174 | if(Opt.and.isatt(Opt,"Mask2D")) then |
---|
1175 | if(.not.all(dimsizes(Opt@Mask2D).eq.latlon_dims)) then |
---|
1176 | print("curvilinear_to_SCRIP: Opt@Mask2D is not the correct dimensionality") |
---|
1177 | exit |
---|
1178 | else |
---|
1179 | mask2d = Opt@Mask2D |
---|
1180 | end if |
---|
1181 | else |
---|
1182 | ;---No masking |
---|
1183 | mask2d = new(latlon_dims, "integer","No_FillValue") |
---|
1184 | mask2d = 1 |
---|
1185 | end if |
---|
1186 | |
---|
1187 | ;---Create the file |
---|
1188 | fid = addfile(FName,"c") |
---|
1189 | setfileoption(fid,"DefineMode",True) |
---|
1190 | |
---|
1191 | ;---Define the file attributes |
---|
1192 | FileAtt = True |
---|
1193 | FileAtt@title = FTitle |
---|
1194 | FileAtt@Conventions = "SCRIP" |
---|
1195 | FileAtt@Createdby = "ESMF_regridding.ncl" |
---|
1196 | FileAtt@date_created = systemfunc("date") |
---|
1197 | fileattdef(fid,FileAtt) |
---|
1198 | |
---|
1199 | ;---Define the SCRIP dimensions |
---|
1200 | grid_size = nlat*nlon ; This is number of data points (grid nodes) |
---|
1201 | grid_corners = 4 |
---|
1202 | grid_rank = 2 |
---|
1203 | |
---|
1204 | FDimNames = (/ "grid_size","grid_corners","grid_rank" /) |
---|
1205 | FDimSizes = (/ grid_size,grid_corners,grid_rank /) |
---|
1206 | FDimUnlim = (/ False,False,False /) |
---|
1207 | |
---|
1208 | filedimdef(fid,FDimNames,FDimSizes,FDimUnlim) |
---|
1209 | |
---|
1210 | ;---Define Variables |
---|
1211 | filevardef(fid,"grid_dims","integer","grid_rank") |
---|
1212 | filevardef(fid,"grid_center_lat","double","grid_size") |
---|
1213 | filevardef(fid,"grid_center_lon","double","grid_size") |
---|
1214 | filevardef(fid,"grid_imask","integer","grid_size") |
---|
1215 | filevardef(fid,"grid_corner_lat","double",(/ "grid_size", "grid_corners" /) ) |
---|
1216 | filevardef(fid,"grid_corner_lon","double",(/ "grid_size", "grid_corners" /) ) |
---|
1217 | |
---|
1218 | ;---Define the variables unit attribute |
---|
1219 | DummyAtt1 = 0 |
---|
1220 | DummyAtt1@units = "degrees" |
---|
1221 | DummyAtt2 = 0 |
---|
1222 | DummyAtt2@units = "unitless" |
---|
1223 | filevarattdef(fid,"grid_center_lat",DummyAtt1) |
---|
1224 | filevarattdef(fid,"grid_center_lon",DummyAtt1) |
---|
1225 | filevarattdef(fid,"grid_imask",DummyAtt2) |
---|
1226 | filevarattdef(fid,"grid_corner_lat",DummyAtt1) |
---|
1227 | filevarattdef(fid,"grid_corner_lon",DummyAtt1) |
---|
1228 | delete(DummyAtt1) |
---|
1229 | delete(DummyAtt2) |
---|
1230 | |
---|
1231 | ;---Prepare the file to store the values |
---|
1232 | setfileoption(fid,"DefineMode",False) |
---|
1233 | |
---|
1234 | ;---Storing Grid Dims |
---|
1235 | fid->grid_dims = (/ nlon, nlat /) ; SCRIP is FORTRAN-based. |
---|
1236 | ; (nlat,nlon) in NCL is equivalent to |
---|
1237 | ; (nlon,nlat) in FORTRAN |
---|
1238 | |
---|
1239 | ;---Store Cell Center Lat/Lon |
---|
1240 | fid->grid_center_lat = (/ndtooned(lat2d)/) |
---|
1241 | fid->grid_center_lon = (/ndtooned(lon2d)/) |
---|
1242 | |
---|
1243 | ;---Store Cell Maskes |
---|
1244 | if (grid_size.ne.dimsizes(ndtooned(mask2d))) then |
---|
1245 | print("curvilinear_to_SCRIP: Mask array is not the appropriate size.") |
---|
1246 | exit |
---|
1247 | else |
---|
1248 | fid->grid_imask=(/ tointeger(ndtooned(mask2d)) /) |
---|
1249 | end if |
---|
1250 | |
---|
1251 | ;---Get the grid lat/lon corners |
---|
1252 | if (isatt(Opt,"GridCornerLat").and.isatt(Opt,"GridCornerLon")) then |
---|
1253 | if(DEBUG) then |
---|
1254 | print("curvilinear_to_SCRIP: using grid corners provided by user...") |
---|
1255 | end if |
---|
1256 | if(.not.any(typeof(Opt@GridCornerLat).eq.(/"float","double"/))) then |
---|
1257 | GridCornerLat = tofloat(Opt@GridCornerLat) |
---|
1258 | else |
---|
1259 | GridCornerLat = Opt@GridCornerLat |
---|
1260 | end if |
---|
1261 | if(.not.any(typeof(Opt@GridCornerLon).eq.(/"float","double"/))) then |
---|
1262 | GridCornerLon = tofloat(Opt@GridCornerLon) |
---|
1263 | else |
---|
1264 | GridCornerLon = Opt@GridCornerLon |
---|
1265 | end if |
---|
1266 | grid_corner_lat = reshape( GridCornerLat,(/ grid_size, grid_corners /)) |
---|
1267 | grid_corner_lon = reshape( GridCornerLon,(/ grid_size, grid_corners /)) |
---|
1268 | else |
---|
1269 | ;---Estimate the grid cell corners; it's better if the user provides them. |
---|
1270 | if(DEBUG) then |
---|
1271 | print("curvilinear_to_SCRIP: calculating grid corners...") |
---|
1272 | end if |
---|
1273 | lat_type = typeof(lat2d) |
---|
1274 | lon_type = typeof(lon2d) |
---|
1275 | if(.not.any(lat_type.eq.(/"float","double"/))) then |
---|
1276 | lat_type = "float" |
---|
1277 | end if |
---|
1278 | if(.not.any(lon_type.eq.(/"float","double"/))) then |
---|
1279 | lon_type = "float" |
---|
1280 | end if |
---|
1281 | grid_corner_lat = new( (/ grid_size, grid_corners /), lat_type) |
---|
1282 | grid_corner_lon = new( (/ grid_size, grid_corners /), lon_type) |
---|
1283 | if(any(fabs(lat2d).eq.90)) then |
---|
1284 | if(DEBUG) then |
---|
1285 | print("curvilinear_to_SCRIP: one or more lat values are at the") |
---|
1286 | print(" poles, so calculating grid corners using") |
---|
1287 | print(" calc_SCRIP_corners_boundaries...") |
---|
1288 | end if |
---|
1289 | calc_SCRIP_corners_boundaries(lat2d,lon2d,grid_corner_lat,grid_corner_lon,Opt) |
---|
1290 | else |
---|
1291 | if(DEBUG) then |
---|
1292 | print("curvilinear_to_SCRIP: no lat values are at the poles, so") |
---|
1293 | print(" calculating grid corners using") |
---|
1294 | print(" calc_SCRIP_corners_noboundaries...") |
---|
1295 | end if |
---|
1296 | calc_SCRIP_corners_noboundaries(lat2d,lon2d,grid_corner_lat,grid_corner_lon,Opt) |
---|
1297 | if(any(fabs(grid_corner_lat).gt.90)) then |
---|
1298 | if(DEBUG) then |
---|
1299 | print("curvilinear_to_SCRIP: calc_SCRIP_corners_noboundaries") |
---|
1300 | print(" produced out-of-range latitude values.") |
---|
1301 | print(" Trying calc_SCRIP_corners_boundaries...") |
---|
1302 | end if |
---|
1303 | calc_SCRIP_corners_boundaries(lat2d,lon2d,grid_corner_lat,grid_corner_lon,Opt) |
---|
1304 | end if |
---|
1305 | end if |
---|
1306 | end if |
---|
1307 | |
---|
1308 | ;---Store the cell corners |
---|
1309 | if (isatt(grid_corner_lat,"_FillValue")) then |
---|
1310 | delete(grid_corner_lat@_FillValue) |
---|
1311 | end if |
---|
1312 | if (isatt(grid_corner_lon,"_FillValue")) then |
---|
1313 | delete(grid_corner_lon@_FillValue) |
---|
1314 | end if |
---|
1315 | fid->grid_corner_lat = (/ todouble(grid_corner_lat) /) |
---|
1316 | fid->grid_corner_lon = (/ todouble(grid_corner_lon) /) |
---|
1317 | |
---|
1318 | if(PrintTimings) then |
---|
1319 | print_elapsed_time(start_time,"curvilinear_to_SCRIP") |
---|
1320 | end if |
---|
1321 | end ; of curvilinear_to_SCRIP(...) |
---|
1322 | |
---|
1323 | ;====================================================================== |
---|
1324 | ; This function accept an array of latitudes and longitudes, creates |
---|
1325 | ; a rectilinear grid based on the given input and then stores it as a |
---|
1326 | ; SCRIP file. This procedure calls curvilinear_to_SCRIP. |
---|
1327 | ;====================================================================== |
---|
1328 | undef("rectilinear_to_SCRIP") |
---|
1329 | procedure rectilinear_to_SCRIP(FName[1]:string,lat[*]:numeric,\ |
---|
1330 | lon[*]:numeric, Opt[1]:logical) |
---|
1331 | local nlat, nlon, grid_center_lat, grid_center_lon, Opt2, FTitle |
---|
1332 | begin |
---|
1333 | Opt2 = Opt |
---|
1334 | |
---|
1335 | ;---Check for options |
---|
1336 | PrintTimings = isatt_logical_true(Opt2,"PrintTimings") |
---|
1337 | if(PrintTimings) then |
---|
1338 | start_time = get_start_time() |
---|
1339 | delete(Opt2@PrintTimings) |
---|
1340 | end if |
---|
1341 | |
---|
1342 | if(.not.isatt(Opt2,"Title")) then |
---|
1343 | Opt2@Title = "rectilinear_to_SCRIP (" + dimsizes(lat) + "," + \ |
---|
1344 | dimsizes(lon) + ")" |
---|
1345 | end if |
---|
1346 | |
---|
1347 | nlat = dimsizes(lat) |
---|
1348 | nlon = dimsizes(lon) |
---|
1349 | |
---|
1350 | ;---Conform the lat/lon to 2D arrays |
---|
1351 | grid_center_lat = conform_dims((/nlat, nlon/),lat,0) |
---|
1352 | grid_center_lon = conform_dims((/nlat, nlon/),lon,1) |
---|
1353 | |
---|
1354 | ;---Generate the mask |
---|
1355 | if(.not.isatt(Opt2,"Mask2D")) then |
---|
1356 | Opt2@Mask2D = onedtond(1,(/nlat,nlon/)) |
---|
1357 | end if |
---|
1358 | |
---|
1359 | ;---Convert to SCRIP |
---|
1360 | curvilinear_to_SCRIP(FName,grid_center_lat,grid_center_lon,Opt2) |
---|
1361 | |
---|
1362 | if(PrintTimings) then |
---|
1363 | print_elapsed_time(start_time,"rectilinear_to_SCRIP") |
---|
1364 | end if |
---|
1365 | end ; of rectilinear_to_SCRIP |
---|
1366 | |
---|
1367 | ;====================================================================== |
---|
1368 | ; This procedure automatically retrieves the grid information from the |
---|
1369 | ; file and converts the grid to a SCRIP file. |
---|
1370 | ; |
---|
1371 | ; The variable must have one of the following: |
---|
1372 | ; - an attribute called "coordinates" which defines |
---|
1373 | ; where the coordinate information are stored. |
---|
1374 | ; - 1D coordinate arrays called "lat" and "lon" |
---|
1375 | ; - 1D coordinate arrays with units that contain "east" and "north" |
---|
1376 | ; - attributes with units that contain "east" and "north" |
---|
1377 | ;====================================================================== |
---|
1378 | undef("auto_to_SCRIP") |
---|
1379 | procedure auto_to_SCRIP(srcFile[1]:string,VarName[1]:string, \ |
---|
1380 | SCRIPFName[1]:string,FTitle[1]:string,Opt[1]:logical) |
---|
1381 | local i, j, fid, nDim, nlat, nlon, VarData, coordType, \ |
---|
1382 | grid_center_lon, grid_center_lat, CoordDim, LonData, LatData, \ |
---|
1383 | LonAttName, LatAttName, tmpAtt, LonDimName, LatDimName, tmpunits, \ |
---|
1384 | tmpdimname, LonVarName, LatVarName, LonVarInd, LatVarInd, \ |
---|
1385 | ResultIsLatorLon, VarNamesInCoordAtt, nCoords, CoordAtt, NumAtt, attNames, \ |
---|
1386 | isLonFound, isLatFound |
---|
1387 | begin |
---|
1388 | ;---Open the file if it exists |
---|
1389 | if (.not.isfilepresent(srcFile)) then |
---|
1390 | print("auto_to_SCRIP: cannot open '" + srcFile + "'.") |
---|
1391 | exit |
---|
1392 | else |
---|
1393 | end if |
---|
1394 | |
---|
1395 | fid = addfile(srcFile,"r") |
---|
1396 | |
---|
1397 | ;---Check if the variable is present in the file |
---|
1398 | if (.not.isfilevar(fid,VarName)) then |
---|
1399 | print("auto_to_SCRIP: the '" + VarName + \ |
---|
1400 | "' variable doesn't exist on the '" + srcFile + "' file.") |
---|
1401 | exit |
---|
1402 | end if |
---|
1403 | |
---|
1404 | VarData = fid->$VarName$ |
---|
1405 | |
---|
1406 | ; |
---|
1407 | ; Check if the lat/lon coordinates are defined for the variable. |
---|
1408 | ; The coordinates can be defined using one of the following methods: |
---|
1409 | ; |
---|
1410 | ; coordType = "coordinate": |
---|
1411 | ; The variable has either: |
---|
1412 | ; - coordinate arrays called "lat" and "lon". |
---|
1413 | ; - coordinate arrays whose units attributes contain |
---|
1414 | ; "north" and "east" |
---|
1415 | ; |
---|
1416 | ; coordType = "variable": |
---|
1417 | ; The coordinates are stored in a separate variable, with the |
---|
1418 | ; name of those variables defined in the "coordinates" |
---|
1419 | ; attribute for the variable. |
---|
1420 | ; |
---|
1421 | ; coordType = "attribute" |
---|
1422 | ; The variable has either: |
---|
1423 | ; - attributes called "lat2d" and "lon2d" |
---|
1424 | ; - attributes whose units attributes contain "north" and "east". |
---|
1425 | ; |
---|
1426 | coordType = "unknown" |
---|
1427 | if( all(iscoord(VarData,(/"lat","lon"/))) ) then |
---|
1428 | coordType = "coordinate" |
---|
1429 | LonDimName = "lon" |
---|
1430 | LatDimName = "lat" |
---|
1431 | end if |
---|
1432 | |
---|
1433 | ;---Check if the coordinate information can be found in the attributes. |
---|
1434 | if (coordType.eq."unknown") then |
---|
1435 | isLatFound = False |
---|
1436 | isLonFound = False |
---|
1437 | attNames = getvaratts(VarData) |
---|
1438 | NumAtt = dimsizes(attNames) |
---|
1439 | do i=0,NumAtt-1 |
---|
1440 | ;---Check if the "coordinate" attribute is set |
---|
1441 | if( isStrSubset(str_lower(attNames(i)),"coordinate") ) then |
---|
1442 | coordType = "variable" |
---|
1443 | CoordAtt = VarData@$attNames(i)$ |
---|
1444 | nCoords = str_fields_count(CoordAtt," ") |
---|
1445 | if (nCoords.lt.2) then |
---|
1446 | print("auto_to_SCRIP: at least two coordinates must be present") |
---|
1447 | exit |
---|
1448 | else |
---|
1449 | VarNamesInCoordAtt = new(nCoords,"string","No_FillValue") |
---|
1450 | do j=0,nCoords-1 |
---|
1451 | VarNamesInCoordAtt(j)=str_get_field(CoordAtt,j+1," ") |
---|
1452 | end do |
---|
1453 | ResultIsLatorLon = isLatorLon(fid,VarNamesInCoordAtt) |
---|
1454 | LatVarInd = ind(ResultIsLatorLon.eq."latitude") |
---|
1455 | LonVarInd = ind(ResultIsLatorLon.eq."longitude") |
---|
1456 | if (.not.ismissing(LatVarInd)) then |
---|
1457 | isLatFound = True |
---|
1458 | LatVarName = VarNamesInCoordAtt(LatVarInd) |
---|
1459 | end if |
---|
1460 | if (.not.ismissing(LonVarInd)) then |
---|
1461 | isLonFound = True |
---|
1462 | LonVarName = VarNamesInCoordAtt(LonVarInd) |
---|
1463 | end if |
---|
1464 | if(.not.(isLonFound.and.isLatFound)) then |
---|
1465 | print("auto_to_SCRIP: cannot locate lat/lon coordinates for ") |
---|
1466 | print(" variable " + VarName + \ |
---|
1467 | " on file " + srcFile) |
---|
1468 | exit |
---|
1469 | end if |
---|
1470 | end if |
---|
1471 | end if |
---|
1472 | end do ; end of checking if the coordinate information can be found in the attributes. |
---|
1473 | end if |
---|
1474 | |
---|
1475 | ;---Check if there is any coordinate dimension |
---|
1476 | if (coordType.eq."unknown") then |
---|
1477 | isLatFound = False |
---|
1478 | isLonFound = False |
---|
1479 | nDim = dimsizes(dimsizes(VarData)) |
---|
1480 | do i=0,nDim-1 |
---|
1481 | if(isdimnamed(VarData,i)) then |
---|
1482 | tmpdimname = VarData!i |
---|
1483 | if (iscoord(VarData,tmpdimname)) then |
---|
1484 | if(isatt(VarData&$tmpdimname$,"units")) then |
---|
1485 | tmpunits = VarData&$tmpdimname$@units |
---|
1486 | if(isStrSubset(tmpunits,"north")) then |
---|
1487 | isLatFound = True |
---|
1488 | LatDimName = tmpdimname |
---|
1489 | end if |
---|
1490 | if(isStrSubset(tmpunits,"east")) then |
---|
1491 | isLonFound = True |
---|
1492 | LonDimName = tmpdimname |
---|
1493 | end if |
---|
1494 | end if |
---|
1495 | end if |
---|
1496 | end if |
---|
1497 | end do |
---|
1498 | if(isLonFound.and.isLatFound) then |
---|
1499 | coordType = "coordinate" |
---|
1500 | end if |
---|
1501 | end if ; end of checking if there is any coordinate dimension |
---|
1502 | ; |
---|
1503 | ; Check if there is any attributes containing the coordinates. |
---|
1504 | ; It won't work if the attributes are stored as a 1D array. |
---|
1505 | ; |
---|
1506 | if (coordType.eq."unknown") then |
---|
1507 | isLatFound = False |
---|
1508 | isLonFound = False |
---|
1509 | do i=0,NumAtt-1 |
---|
1510 | tmpAtt = VarData@$attNames(i)$ |
---|
1511 | if(isatt(tmpAtt,"units")) then |
---|
1512 | tmpunits = tmpAtt@units |
---|
1513 | if(isStrSubset(tmpunits,"north")) then |
---|
1514 | isLatFound = True |
---|
1515 | LatAttName = attNames(i) |
---|
1516 | end if |
---|
1517 | if(isStrSubset(tmpunits,"east")) then |
---|
1518 | isLonFound = True |
---|
1519 | LonAttName = attNames(i) |
---|
1520 | end if |
---|
1521 | end if |
---|
1522 | end do |
---|
1523 | if(isLonFound.and.isLatFound) then |
---|
1524 | coordType = "attribute" |
---|
1525 | end if |
---|
1526 | end if ; end of checking if there is any attributes containing the coordinates. |
---|
1527 | if(.not.(isLonFound.and.isLatFound)) then |
---|
1528 | print("auto_to_SCRIP: cannot locate lat/lon coordinates for ") |
---|
1529 | print(" variable " + VarName + " on file " + srcFile) |
---|
1530 | exit |
---|
1531 | end if |
---|
1532 | |
---|
1533 | ;---Get the coordinate variable names and coordinate data |
---|
1534 | if (coordType.eq."variable") then |
---|
1535 | if(isfilevar(fid,LatVarName)) then |
---|
1536 | LatData = fid->$LatVarName$ |
---|
1537 | else |
---|
1538 | print("auto_to_SCRIP: can't read latitude information.") |
---|
1539 | exit |
---|
1540 | end if |
---|
1541 | if(isfilevar(fid,LonVarName)) then |
---|
1542 | LonData = fid->$LonVarName$ |
---|
1543 | else |
---|
1544 | print("auto_to_SCRIP: can't read longitude information.") |
---|
1545 | exit |
---|
1546 | end if |
---|
1547 | else if (coordType.eq."coordinate") then |
---|
1548 | nCoords = dimsizes(VarData) |
---|
1549 | LonData = VarData&$LonDimName$ |
---|
1550 | LatData = VarData&$LatDimName$ |
---|
1551 | else ; coordType.eq."attribute" |
---|
1552 | LonData = VarData@$LonAttName$ |
---|
1553 | LatData = VarData@$LatAttName$ |
---|
1554 | if ((dimsizes(dimsizes(LonData)).ne.2).or.\ |
---|
1555 | (dimsizes(dimsizes(LatData)))) then |
---|
1556 | print("auto_to_SCRIP: " + LonAttName + " and " + \ |
---|
1557 | LatAttName + " must be 2D.") |
---|
1558 | exit |
---|
1559 | end if |
---|
1560 | end if |
---|
1561 | end if ; end of getting coordinate variables names and data |
---|
1562 | ; at this point lat/lon data is stored in LatData and LonData |
---|
1563 | |
---|
1564 | if (.not.all(dimsizes(LonData).eq.dimsizes(LatData))) then |
---|
1565 | print("auto_to_SCRIP: latitude and longitude must have the same number of dimensions.") |
---|
1566 | exit |
---|
1567 | end if |
---|
1568 | |
---|
1569 | CoordDim = dimsizes(dimsizes(LonData)) |
---|
1570 | if ((CoordDim.eq.1).and.(coordType.eq."coordinate")) then |
---|
1571 | nlat = dimsizes(LatData) |
---|
1572 | nlon = dimsizes(LonData) |
---|
1573 | ;---Generate the grid centers |
---|
1574 | grid_center_lat = conform_dims((/nlat, nlon/),LatData,0) |
---|
1575 | grid_center_lon = conform_dims((/nlat, nlon/),LonData,1) |
---|
1576 | else if ((CoordDim.eq.2).and.((coordType.eq."attribute").or. \ |
---|
1577 | (coordType.eq."variable"))) then |
---|
1578 | grid_center_lat = LatData |
---|
1579 | grid_center_lon = LonData |
---|
1580 | delete(LatData) |
---|
1581 | delete(LonData) |
---|
1582 | else |
---|
1583 | print("auto_to_SCRIP: Can't help with recognizing the coordinates.") |
---|
1584 | exit |
---|
1585 | end if |
---|
1586 | end if |
---|
1587 | |
---|
1588 | ;---Get mask |
---|
1589 | if (.not.isatt(Opt,"Mask2D")) then |
---|
1590 | Opt@Mask2D = new(dimsizes(grid_center_lat),"integer","No_FillValue") |
---|
1591 | Opt@Mask2D = 1 |
---|
1592 | end if |
---|
1593 | Opt@Title = FTitle |
---|
1594 | |
---|
1595 | curvilinear_to_SCRIP(SCRIPFName,grid_center_lat,grid_center_lon,Opt) |
---|
1596 | end ; of auto_to_SCRIP |
---|
1597 | |
---|
1598 | ;====================================================================== |
---|
1599 | ; This procedure generates a regularly-spaced lat/lon grid based on |
---|
1600 | ; the corners of a lat/lon box, or a box center and the width |
---|
1601 | ; and height of the box and the spacing or the number of values. |
---|
1602 | ; |
---|
1603 | ; It was overhauled on Jan 10 2012 to allow for input of a grid type |
---|
1604 | ; rather than inputing nlat and nlon. |
---|
1605 | ; |
---|
1606 | ; GridType can be one of the following types of input: |
---|
1607 | ; |
---|
1608 | ; "1x1", "2x3", "0.25x0.25", etc |
---|
1609 | ; "1deg", "0.25deg" "0.25 deg" "0.25" (which means "0.25deg") |
---|
1610 | ; "G64", "G128" |
---|
1611 | ; |
---|
1612 | ; Recognized attributes for "Opt": |
---|
1613 | ; LLCorner - defaults to (-90,-180) |
---|
1614 | ; URCorner - defaults to ( 90, 180) |
---|
1615 | ; Rotation - rotation angle |
---|
1616 | ; |
---|
1617 | ; It stores the grid in a NetCDF file based on SCRIP standard. |
---|
1618 | ; |
---|
1619 | ; This procedure calls curvilinear_to_SCRIP. |
---|
1620 | ;====================================================================== |
---|
1621 | undef("latlon_to_SCRIP") |
---|
1622 | procedure latlon_to_SCRIP(FName[1]:string,GridType[1]:string,Opt[1]:logical) |
---|
1623 | local BoxDiag, Rot, nlat, nlon, grid_type, dlat, dlon,\ |
---|
1624 | lat, lon, gau_info, grid_center_lat, grid_center_lon, FTitle, \ |
---|
1625 | PrintTimings, set_pt, orig_pt |
---|
1626 | begin |
---|
1627 | PrintTimings = isatt_logical_true(Opt,"PrintTimings") |
---|
1628 | if(PrintTimings) then |
---|
1629 | start_time = get_start_time() |
---|
1630 | end if |
---|
1631 | |
---|
1632 | ;---Check for "1x1", "2x3", "0.25x0.25" format |
---|
1633 | grid_type = check_grid_type(GridType) |
---|
1634 | if(grid_type.eq."none") then |
---|
1635 | print("latlon_to_SCRIP: invalid format for grid type") |
---|
1636 | exit |
---|
1637 | end if |
---|
1638 | |
---|
1639 | if(grid_type.eq."degree") then |
---|
1640 | dlat = grid_type@dlat |
---|
1641 | dlon = grid_type@dlon |
---|
1642 | else if(grid_type.eq."gaussian") then |
---|
1643 | nlat = grid_type@nlat |
---|
1644 | nlon = grid_type@nlon |
---|
1645 | end if |
---|
1646 | end if |
---|
1647 | |
---|
1648 | ;---Check for Opt attributes |
---|
1649 | if(Opt.and.isatt(Opt,"Title")) then |
---|
1650 | FTitle = Opt@Title |
---|
1651 | else |
---|
1652 | FTitle = GridType + " grid" |
---|
1653 | end if |
---|
1654 | |
---|
1655 | if (Opt.and.(isatt(Opt,"Rotation"))) then |
---|
1656 | Rot = Opt@Rotation |
---|
1657 | else |
---|
1658 | Rot = 0.0 |
---|
1659 | end if |
---|
1660 | |
---|
1661 | if(.not.isvar("LLCorner")) then |
---|
1662 | if(isatt(Opt,"LLCorner")) then |
---|
1663 | LLCorner = Opt@LLCorner |
---|
1664 | else |
---|
1665 | LLCorner = (/-90,-180/) |
---|
1666 | end if |
---|
1667 | end if |
---|
1668 | |
---|
1669 | if(.not.isvar("URCorner")) then |
---|
1670 | if(isatt(Opt,"URCorner")) then |
---|
1671 | URCorner = Opt@URCorner |
---|
1672 | else |
---|
1673 | URCorner = (/90,180/) |
---|
1674 | end if |
---|
1675 | end if |
---|
1676 | |
---|
1677 | ;---Check the LLCorner and URCorner variables. |
---|
1678 | if ( (abs(LLCorner(0)).gt.90).or.(abs(URCorner(0)).gt.90) ) then |
---|
1679 | print("latlon_to_SCRIP: lat corners must be within -90 to 90") |
---|
1680 | exit |
---|
1681 | end if |
---|
1682 | |
---|
1683 | BoxDiag = URCorner-LLCorner |
---|
1684 | if (Opt.and.(isatt(Opt,"Rotation"))) then |
---|
1685 | Rot = Opt@Rotation |
---|
1686 | if (Rot.ne.0.0) then |
---|
1687 | ; |
---|
1688 | ; Align the box diagonal to make box sides parallel/perpendicular |
---|
1689 | ; to equator. |
---|
1690 | ; |
---|
1691 | rotate_latlon(BoxDiag(0),BoxDiag(1),-1.0*Rot) |
---|
1692 | end if |
---|
1693 | else |
---|
1694 | Rot = 0.0 |
---|
1695 | end if |
---|
1696 | |
---|
1697 | if ( BoxDiag(0).le.0.0 ) then |
---|
1698 | print("latlon_to_SCRIP: The corner latitudes do not meet the criteria of a bounding box.") |
---|
1699 | exit |
---|
1700 | end if |
---|
1701 | if ( BoxDiag(1).le.0.0 ) then |
---|
1702 | print("latlon_to_SCRIP: The corner longitudes do not meet the criteria of a bounding box.") |
---|
1703 | exit |
---|
1704 | end if |
---|
1705 | |
---|
1706 | if (Rot.ne.0.0) then |
---|
1707 | print("latlon_to_SCRIP: The box is rotated. Spacing may be different than what you meant.") |
---|
1708 | end if |
---|
1709 | if(grid_type.eq."degree") then |
---|
1710 | nlat = tointeger(BoxDiag(0)/dlat+1) |
---|
1711 | nlon = tointeger(BoxDiag(1)/dlon+1) |
---|
1712 | |
---|
1713 | lat = fspan(0.0,BoxDiag(0),nlat) |
---|
1714 | lon = fspan(0.0,BoxDiag(1),nlon) |
---|
1715 | else if(grid_type.eq."gaussian") then |
---|
1716 | gau_info = gaus(nlat/2) |
---|
1717 | lat = gau_info(:,0) ; gaussian latitudes |
---|
1718 | lon = todouble((/lonGlobeFo(nlon,"","","")/)) |
---|
1719 | end if |
---|
1720 | end if |
---|
1721 | |
---|
1722 | ;---Generate the cell center Lat/Lon |
---|
1723 | grid_center_lat = conform_dims((/nlat, nlon/),lat,0) |
---|
1724 | grid_center_lon = conform_dims((/nlat, nlon/),lon,1) |
---|
1725 | |
---|
1726 | if(.not.isatt(Opt,"Mask2D")) then |
---|
1727 | Opt@Mask2D = onedtond(1,(/nlat,nlon/)) |
---|
1728 | end if |
---|
1729 | |
---|
1730 | ;---Rotate back the box, if needed. |
---|
1731 | if (Rot.ne.(0.0)) then |
---|
1732 | rotate_latlon(grid_center_lat,grid_center_lon,Rot) |
---|
1733 | end if |
---|
1734 | |
---|
1735 | ;---Now that the rotation was successful translating it back. |
---|
1736 | if(grid_type.ne."gaussian") then |
---|
1737 | grid_center_lat = grid_center_lat + LLCorner(0) |
---|
1738 | grid_center_lon = grid_center_lon + LLCorner(1) |
---|
1739 | end if |
---|
1740 | |
---|
1741 | Opt@Title = FTitle |
---|
1742 | |
---|
1743 | ;---Make sure curvilinear_to_SCRIP doesn't print timings. |
---|
1744 | set_pt = False |
---|
1745 | if(isatt(Opt,"PrintTimings")) then |
---|
1746 | set_pt = True |
---|
1747 | orig_pt = Opt@PrintTimings |
---|
1748 | Opt@PrintTimings = False |
---|
1749 | end if |
---|
1750 | curvilinear_to_SCRIP(FName,grid_center_lat,grid_center_lon,Opt) |
---|
1751 | |
---|
1752 | if(set_pt) then |
---|
1753 | Opt@PrintTimings = orig_pt |
---|
1754 | end if |
---|
1755 | |
---|
1756 | if(PrintTimings) then |
---|
1757 | print_elapsed_time(start_time,"latlon_to_SCRIP") |
---|
1758 | end if |
---|
1759 | end ; of latlon_to_SCRIP(...) |
---|
1760 | |
---|
1761 | ;====================================================================== |
---|
1762 | ; Checks if the required dimensions in the SCRIP file are present |
---|
1763 | ;====================================================================== |
---|
1764 | undef("check_SCRIP_dims") |
---|
1765 | function check_SCRIP_dims(fid[1]:file,verbose[1]:logical) |
---|
1766 | local i, file_dims, scrip_dim_names |
---|
1767 | begin |
---|
1768 | file_dims = getvardims(fid) |
---|
1769 | scrip_dim_names = (/"grid_rank","grid_size","grid_corners"/) |
---|
1770 | do i=0,dimsizes(scrip_dim_names)-1 |
---|
1771 | if (.not.(any(file_dims.eq.scrip_dim_names(i)))) |
---|
1772 | if(verbose) then |
---|
1773 | print("check_SCRIP_dims: " + scrip_dim_names(i) + " not defined.") |
---|
1774 | end if |
---|
1775 | return(False) |
---|
1776 | end if |
---|
1777 | end do |
---|
1778 | return(True) |
---|
1779 | end ; of check_SCRIP_dims(...) |
---|
1780 | |
---|
1781 | ;====================================================================== |
---|
1782 | ; Checks if the required variables in SCRIP file are present |
---|
1783 | ;====================================================================== |
---|
1784 | undef("check_SCRIP_vars") |
---|
1785 | function check_SCRIP_vars(fid[1]:file,verbose[1]:logical) |
---|
1786 | local i, scrip_vars |
---|
1787 | begin |
---|
1788 | scrip_vars = (/"grid_dims", "grid_center_lat", "grid_center_lon",\ |
---|
1789 | "grid_imask", "grid_corner_lat", "grid_corner_lon"/) |
---|
1790 | do i=0,dimsizes(scrip_vars)-1 |
---|
1791 | if(.not.(isfilevar(fid,scrip_vars(i)))) |
---|
1792 | if(verbose) then |
---|
1793 | print("check_SCRIP_vars: " + scrip_vars(i) + " not defined.") |
---|
1794 | end if |
---|
1795 | return(False) |
---|
1796 | end if |
---|
1797 | end do |
---|
1798 | return(True) |
---|
1799 | end ; check_SCRIP_vars(...) |
---|
1800 | |
---|
1801 | ;====================================================================== |
---|
1802 | ; Checks if the input filename is following SCRIP standard |
---|
1803 | ;====================================================================== |
---|
1804 | undef("is_SCRIP") |
---|
1805 | function is_SCRIP(FileName[1]:string,verbose[1]:logical) |
---|
1806 | local fid |
---|
1807 | begin |
---|
1808 | if (.not.isfilepresent(FileName)) then |
---|
1809 | if(verbose) then |
---|
1810 | print("is_SCRIP: '" + FileName + "' doesn't exist.") |
---|
1811 | end if |
---|
1812 | return(False) |
---|
1813 | end if |
---|
1814 | fid = addfile(FileName,"r") |
---|
1815 | return check_SCRIP_dims(fid,verbose).and.check_SCRIP_vars(fid,verbose) |
---|
1816 | |
---|
1817 | end ; of is_SCRIP(...) |
---|
1818 | |
---|
1819 | ;====================================================================== |
---|
1820 | ; Checks if the required dimensions in the ESMF file are present |
---|
1821 | ;====================================================================== |
---|
1822 | undef("check_ESMF_dims") |
---|
1823 | function check_ESMF_dims(fid[1]:file,verbose[1]:logical) |
---|
1824 | local i, esmf_dim_names, file_dims |
---|
1825 | begin |
---|
1826 | file_dims = getvardims(fid) |
---|
1827 | esmf_dim_names = (/"nodeCount", "elementCount", \ |
---|
1828 | "maxNodePElement","coordDim"/) |
---|
1829 | |
---|
1830 | do i=0,dimsizes(esmf_dim_names)-1 |
---|
1831 | if (.not.(any(file_dims.eq.esmf_dim_names(i)))) |
---|
1832 | if(verbose) then |
---|
1833 | print("check_ESMF_dims: " + esmf_dim_names(i) + " not defined.") |
---|
1834 | end if |
---|
1835 | return(False) |
---|
1836 | end if |
---|
1837 | end do |
---|
1838 | return(True) |
---|
1839 | end ; of check_ESMF_dims(...) |
---|
1840 | |
---|
1841 | ;====================================================================== |
---|
1842 | ; Checks if the required variables in ESMF file are present |
---|
1843 | ;====================================================================== |
---|
1844 | undef("check_ESMF_vars") |
---|
1845 | function check_ESMF_vars(fid[1]:file,verbose[1]:logical) |
---|
1846 | local i, esmf_vars |
---|
1847 | begin |
---|
1848 | esmf_vars = (/"nodeCoords","elementConn","numElementConn",\ |
---|
1849 | "centerCoords","elementArea","elementMask"/) |
---|
1850 | |
---|
1851 | do i=0,dimsizes(esmf_vars)-1 |
---|
1852 | if(.not.(isfilevar(fid,esmf_vars(i)))) |
---|
1853 | if(verbose) then |
---|
1854 | print("check_ESMF_vars: " + esmf_vars(i) + " not defined.") |
---|
1855 | end if |
---|
1856 | return(False) |
---|
1857 | end if |
---|
1858 | end do |
---|
1859 | return(True) |
---|
1860 | end ; check_ESMF_vars(...) |
---|
1861 | |
---|
1862 | ;====================================================================== |
---|
1863 | ; Checks if the input filename is following ESMF standard |
---|
1864 | ;====================================================================== |
---|
1865 | undef("is_ESMF") |
---|
1866 | function is_ESMF(FileName[1]:string,verbose[1]:logical) |
---|
1867 | local fid |
---|
1868 | begin |
---|
1869 | if (.not.isfilepresent(FileName)) then |
---|
1870 | if(verbose) then |
---|
1871 | print("is_ESMF: '" + FileName + "' doesn't exist.") |
---|
1872 | end if |
---|
1873 | return(False) |
---|
1874 | end if |
---|
1875 | fid = addfile(FileName,"r") |
---|
1876 | return check_ESMF_dims(fid,verbose).and.check_ESMF_vars(fid,verbose) |
---|
1877 | |
---|
1878 | end ; of is_ESMF(...) |
---|
1879 | |
---|
1880 | ;====================================================================== |
---|
1881 | ; This function will retrieve the latitude coordinate of a grid |
---|
1882 | ; from a SCRIP formatted file. |
---|
1883 | ; |
---|
1884 | ; This routine is obsolete in V6.1.0 (not in V6.1.0-beta). It's |
---|
1885 | ; replaced by ESMF_copy_varcoords. |
---|
1886 | ;====================================================================== |
---|
1887 | undef("retrieve_SCRIP_lat") |
---|
1888 | function retrieve_SCRIP_lat(fileName[1]:string) |
---|
1889 | local fid, grid_dims, grid_center_lat |
---|
1890 | begin |
---|
1891 | if (.not.isfilepresent(fileName)) then |
---|
1892 | print("retrieve_SCRIP_lat: cannot open '" + fileName + "'.") |
---|
1893 | exit |
---|
1894 | end if |
---|
1895 | |
---|
1896 | fid = addfile(fileName,"r") |
---|
1897 | grid_dims = fid->grid_dims |
---|
1898 | grid_center_lat = reshape( fid->grid_center_lat , grid_dims(::-1)) |
---|
1899 | grid_center_lat@units="degrees_north" |
---|
1900 | |
---|
1901 | return(grid_center_lat) |
---|
1902 | end ; of retrieve_SCRIP_lat |
---|
1903 | |
---|
1904 | ;====================================================================== |
---|
1905 | ; This function will retrieve the longitude coordinate of a grid |
---|
1906 | ; from a SCRIP file. |
---|
1907 | ; |
---|
1908 | ; This routine is obsolete in V6.1.0 (not in V6.1.0-beta). It's |
---|
1909 | ; replaced by ESMF_copy_varcoords. |
---|
1910 | ;====================================================================== |
---|
1911 | undef("retrieve_SCRIP_lon") |
---|
1912 | function retrieve_SCRIP_lon(fileName[1]:string) |
---|
1913 | local fid, grid_dims, grid_center_lon |
---|
1914 | begin |
---|
1915 | if (.not.isfilepresent(fileName)) then |
---|
1916 | print("retrieve_SCRIP_lon: cannot open '" + fileName + "'.") |
---|
1917 | exit |
---|
1918 | end if |
---|
1919 | |
---|
1920 | fid = addfile(fileName,"r") |
---|
1921 | grid_dims = fid->grid_dims; |
---|
1922 | grid_center_lon = reshape( fid->grid_center_lon , grid_dims(::-1)) |
---|
1923 | grid_center_lon@units="degrees_east" |
---|
1924 | |
---|
1925 | return(grid_center_lon) |
---|
1926 | end ; of retrieve_SCRIP_lon |
---|
1927 | |
---|
1928 | ;====================================================================== |
---|
1929 | ; This function will retrieve the latitude values from an ESMF |
---|
1930 | ; formatted file. |
---|
1931 | ; |
---|
1932 | ; This routine is obsolete in V6.1.0 (not in V6.1.0-beta). It's |
---|
1933 | ; replaced by ESMF_copy_varcoords. |
---|
1934 | ;====================================================================== |
---|
1935 | undef("retrieve_ESMF_lat") |
---|
1936 | function retrieve_ESMF_lat(fileName[1]:string) |
---|
1937 | local fid, grid_center_lon |
---|
1938 | begin |
---|
1939 | if (.not.isfilepresent(fileName)) then |
---|
1940 | print("retrieve_ESMF_lat: cannot open '"+ fileName + "'.") |
---|
1941 | exit |
---|
1942 | end if |
---|
1943 | |
---|
1944 | fid = addfile(fileName,"r") |
---|
1945 | grid_center_lat = fid->centerCoords(:,1) |
---|
1946 | grid_center_lat@units="degrees_north" |
---|
1947 | |
---|
1948 | return(grid_center_lat) |
---|
1949 | end ; of retrieve_ESMF_lat |
---|
1950 | |
---|
1951 | ;====================================================================== |
---|
1952 | ; This function will retrieve the longitude values from an ESMF |
---|
1953 | ; formatted file. |
---|
1954 | ; |
---|
1955 | ; This routine is obsolete in V6.1.0 (not in V6.1.0-beta). It's |
---|
1956 | ; replaced by ESMF_copy_varcoords. |
---|
1957 | ;====================================================================== |
---|
1958 | undef("retrieve_ESMF_lon") |
---|
1959 | function retrieve_ESMF_lon(fileName[1]:string) |
---|
1960 | local fid, grid_center_lon |
---|
1961 | begin |
---|
1962 | if (.not.isfilepresent(fileName)) then |
---|
1963 | print("retrieve_ESMF_lon: cannot open '" + fileName + "'.") |
---|
1964 | exit |
---|
1965 | end if |
---|
1966 | |
---|
1967 | fid = addfile(fileName,"r") |
---|
1968 | grid_center_lon = fid->centerCoords(:,0) |
---|
1969 | grid_center_lon@units="degrees_east" |
---|
1970 | |
---|
1971 | return(grid_center_lon) |
---|
1972 | end ; of retrieve_ESMF_lon |
---|
1973 | |
---|
1974 | ;====================================================================== |
---|
1975 | ; This procedure will retrieve the destination lat/lon values from a |
---|
1976 | ; weights file generated by ESMF_RegridWeightGen and attach them |
---|
1977 | ; to the given variable. Since rectilinear lat/lon grids are written |
---|
1978 | ; as 2D arrays, you can't tell if it's rectilinear or curvilinear, |
---|
1979 | ; unless you test for repetitive values. |
---|
1980 | ; |
---|
1981 | ; To force this routine to not check for "rectilinear-ness", you |
---|
1982 | ; can set opt@DstGridType to "rectilinear". |
---|
1983 | ; |
---|
1984 | ; There's no standard way to attached unstructured lat/lon data to |
---|
1985 | ; a variable, so we do it by "lat1d" and "lon1d" undef. |
---|
1986 | ;====================================================================== |
---|
1987 | undef("ESMF_copy_varcoords") |
---|
1988 | procedure ESMF_copy_varcoords(sd:numeric,sd_regrid:numeric,\ |
---|
1989 | wgt_file_name[1]:string,opt[1]:logical) |
---|
1990 | local fwgt, src_dims, src_ranks, dst_dims, dst_rank, mlon, nlat, i, ii, \ |
---|
1991 | dst_grid_dims, dst_grid_rank, lat, lon, nleftmost, nleftdims, DEBUG |
---|
1992 | begin |
---|
1993 | DEBUG = isatt_logical_true(opt,"Debug") |
---|
1994 | |
---|
1995 | ;---Error checking |
---|
1996 | if (.not.isfilepresent(wgt_file_name)) then |
---|
1997 | print("Warning: ESMF_copy_varcoords: cannot open '" + wgt_file_name + "'.") |
---|
1998 | print(" No coordinate information will be added.") |
---|
1999 | return |
---|
2000 | end if |
---|
2001 | |
---|
2002 | ;---Get size of source and destination grids from weight file |
---|
2003 | fwgt = addfile(wgt_file_name, "r") |
---|
2004 | src_grid_dims = fwgt->src_grid_dims(::-1) ; dimensions are Fortran based |
---|
2005 | dst_grid_dims = fwgt->dst_grid_dims(::-1) |
---|
2006 | src_grid_rank = dimsizes(src_grid_dims) |
---|
2007 | dst_grid_rank = dimsizes(dst_grid_dims) |
---|
2008 | |
---|
2009 | ;---More error checking |
---|
2010 | if(dst_grid_rank.ne.1.and.dst_grid_rank.ne.2) then |
---|
2011 | print("Warning: ESMF_copy_varcoords: the rank of the lat/lon values") |
---|
2012 | print(" on the weights file must be 1 or 2.") |
---|
2013 | print(" No coordinate information will be added.") |
---|
2014 | return |
---|
2015 | end if |
---|
2016 | |
---|
2017 | if(isatt(opt,"DstGridType")) then |
---|
2018 | if(check_grid_type(opt@DstGridType).eq."none") then |
---|
2019 | print("Warning: ESMF_copy_varcoords: don't recognize the grid type") |
---|
2020 | print(" specified: '" + opt@DstGridType + "'.") |
---|
2021 | print(" No coordinate information will be added.") |
---|
2022 | return |
---|
2023 | end if |
---|
2024 | if(opt@DstGridType.eq."unstructured".and.dst_grid_rank.ne.1) then |
---|
2025 | print("Warning: ESMF_copy_varcoords: you specified an unstructured") |
---|
2026 | print(" grid, but your lat/lon coordinates appear to be") |
---|
2027 | print(" curvilinear or rectilinear.") |
---|
2028 | print(" No coordinate information will be added.") |
---|
2029 | return |
---|
2030 | end if |
---|
2031 | if(any(opt@DstGridType.eq.(/"curvilinear","rectilinear"/)).and.\ |
---|
2032 | dst_grid_rank.ne.2) then |
---|
2033 | print("Warning: ESMF_copy_varcoords: you specified a rectilinear") |
---|
2034 | print(" or curvilinear grid, but your lat/lon coordinates") |
---|
2035 | print(" appear to be unstructured.") |
---|
2036 | print(" No coordinate information will be added.") |
---|
2037 | return |
---|
2038 | end if |
---|
2039 | end if |
---|
2040 | |
---|
2041 | ;---Get size of original and regridded variables |
---|
2042 | src_dims = dimsizes(sd) |
---|
2043 | src_rank = dimsizes(src_dims) |
---|
2044 | dst_dims = dimsizes(sd_regrid) |
---|
2045 | dst_rank = dimsizes(dst_dims) |
---|
2046 | |
---|
2047 | ;---Both grids must have the same leftmost dimemsions |
---|
2048 | if((src_rank-src_grid_rank).ne.(dst_rank-dst_grid_rank)) then |
---|
2049 | print("Warning: ESMF_copy_varcoords: the dimension sizes of your") |
---|
2050 | print(" source and destination grids don't match up.") |
---|
2051 | print(" No coordinate information will be added.") |
---|
2052 | return |
---|
2053 | end if |
---|
2054 | ;---Make sure leftmost dimensions are same size. |
---|
2055 | nleftdims = src_rank-src_grid_rank |
---|
2056 | do i=0,nleftdims-1 |
---|
2057 | if(src_dims(i).ne.dst_dims(i)) then |
---|
2058 | print("Warning: ESMF_copy_varcoords: the dimension sizes of your") |
---|
2059 | print(" source and destination grids don't match up.") |
---|
2060 | print(" No coordinate information will be added.") |
---|
2061 | return |
---|
2062 | end if |
---|
2063 | end do |
---|
2064 | |
---|
2065 | ;---Done with error checking. Copy the leftmost coordinates, if any. |
---|
2066 | if(nleftdims.gt.0) then |
---|
2067 | ii = ispan(0,nleftdims-1,1) |
---|
2068 | copy_VarCoords_n(sd,sd_regrid,ii) |
---|
2069 | end if |
---|
2070 | ; |
---|
2071 | ; Test for what kind of coordinate information to add. |
---|
2072 | ; |
---|
2073 | if (dst_grid_rank.eq.1) then |
---|
2074 | sd_regrid@lat1d = fwgt->yc_b ; I don't like this, but how else to |
---|
2075 | sd_regrid@lon1d = fwgt->xc_b ; attach? |
---|
2076 | return |
---|
2077 | end if |
---|
2078 | |
---|
2079 | ;---dst_grid_rank must be 2 |
---|
2080 | nlat = dst_grid_dims(0) |
---|
2081 | mlon = dst_grid_dims(1) |
---|
2082 | |
---|
2083 | if(isatt(opt,"DstGridType").and.opt@DstGridType.eq."curvilinear") then |
---|
2084 | sd_regrid@lat2d = reshape(fwgt->yc_b,(/nlat,mlon/)) |
---|
2085 | sd_regrid@lon2d = reshape(fwgt->xc_b,(/nlat,mlon/)) |
---|
2086 | return |
---|
2087 | end if |
---|
2088 | ; |
---|
2089 | ; If DstGridType not specified, then we have to check if these are |
---|
2090 | ; rectilinear coordinates. This can be an expensive operation. |
---|
2091 | ; I'm not sure if this should be the default. |
---|
2092 | ; |
---|
2093 | is_rectilinear = isatt(opt,"DstGridType").and.\ |
---|
2094 | opt@DstGridType.eq."rectilinear" |
---|
2095 | |
---|
2096 | lat = fwgt->yc_b(0::mlon) |
---|
2097 | lon = fwgt->xc_b(0:mlon-1) |
---|
2098 | |
---|
2099 | if(.not.is_rectilinear) then |
---|
2100 | tmplat = fwgt->yc_b(nlat/2::mlon) |
---|
2101 | tmplon = fwgt->xc_b(mlon:2*mlon-1) |
---|
2102 | nlat_test = dimsizes(lat) |
---|
2103 | nlon_test = dimsizes(lon) |
---|
2104 | nlat_tmp = dimsizes(tmplat) |
---|
2105 | nlon_tmp = dimsizes(tmplon) |
---|
2106 | ;---Test for "rectilinear-ness" |
---|
2107 | if (nlat_test.eq.nlat_tmp.and.nlon_test.eq.nlon_tmp.and.\ |
---|
2108 | all(lat.eq.tmplat).and.all(lon.eq.tmplon)) then |
---|
2109 | if(DEBUG) then |
---|
2110 | print("ESMF_copy_varcoords: detected a rectilinear grid.") |
---|
2111 | dq = str_get_dq() |
---|
2112 | print(" If you know your grid is rectilinear, then set") |
---|
2113 | print(" opt@DstGridType = " + dq + "rectilinear" + dq + " to") |
---|
2114 | print(" prevent this potentially slow test.") |
---|
2115 | end if |
---|
2116 | is_rectilinear = True |
---|
2117 | else |
---|
2118 | sd_regrid@lat2d = reshape(fwgt->yc_b,(/nlat,mlon/)) |
---|
2119 | sd_regrid@lon2d = reshape(fwgt->xc_b,(/nlat,mlon/)) |
---|
2120 | return |
---|
2121 | end if |
---|
2122 | delete([/tmplat,tmplon/]) |
---|
2123 | end if |
---|
2124 | |
---|
2125 | ;---Must be rectilinear! |
---|
2126 | lat@long_name = "latitude" |
---|
2127 | lat@units = "degrees_north" |
---|
2128 | lat!0 = "lat" |
---|
2129 | lat&lat = lat |
---|
2130 | |
---|
2131 | lon = fwgt->xc_b(0:mlon-1) |
---|
2132 | lon@long_name = "longitude" |
---|
2133 | lon@units = "degrees_east" |
---|
2134 | lon!0 = "lon" |
---|
2135 | lon&lon = lon |
---|
2136 | |
---|
2137 | dst_dims = dimsizes(sd_regrid) |
---|
2138 | dst_rank = dimsizes(dst_dims) |
---|
2139 | sd_regrid!(dst_rank-2) = "lat" |
---|
2140 | sd_regrid!(dst_rank-1) = "lon" |
---|
2141 | sd_regrid&lat = lat |
---|
2142 | sd_regrid&lon = lon |
---|
2143 | return |
---|
2144 | end |
---|
2145 | |
---|
2146 | |
---|
2147 | ;====================================================================== |
---|
2148 | ; This procedure copies attributes from the original variable to |
---|
2149 | ; the regridded variable, and attaches coordinate information, if |
---|
2150 | ; available. It will not copy lat1d/lon1d or lat2d/lon2d attributes. |
---|
2151 | ; |
---|
2152 | ; The coordinate information is retrieved from the weights file. |
---|
2153 | ; |
---|
2154 | ; I think this procedure needs to have added to it the ability to |
---|
2155 | ; have a "coordinates" attribute for the case of curvilinear grids. |
---|
2156 | ;====================================================================== |
---|
2157 | undef("ESMF_copy_varmeta") |
---|
2158 | procedure ESMF_copy_varmeta(sd,sd_regrid,wgt_file_name[1]:string, \ |
---|
2159 | opt[1]:logical) |
---|
2160 | local var_dims, var_rank, dstlat, dstlon, atts_to_skip, natts |
---|
2161 | begin |
---|
2162 | var_dims = dimsizes(sd_regrid) |
---|
2163 | var_rank = dimsizes(var_dims) |
---|
2164 | |
---|
2165 | ;---Copy variable attributes, unless requested otherwise (True by default) |
---|
2166 | if(.not.isatt_logical_false(opt,"CopyVarAtts").and.\ |
---|
2167 | .not.isatt_logical_false(opt,"CopyVarMeta")) then |
---|
2168 | atts_to_skip = (/"lat1d","lon1d","lat2d","lon2d","coordinates"/) |
---|
2169 | if(isatt(opt,"CopyVarAttsExcept")) then |
---|
2170 | natts = dimsizes(atts_to_skip) |
---|
2171 | new_atts_to_skip = new(natts+dimsizes(opt@CopyVarAttsExcept),string) |
---|
2172 | new_atts_to_skip(0:natts-1) = atts_to_skip |
---|
2173 | new_atts_to_skip(natts:) = opt@CopyVarAttsExcept |
---|
2174 | copy_VarAtts_except(sd,sd_regrid,new_atts_to_skip) |
---|
2175 | else |
---|
2176 | copy_VarAtts_except(sd,sd_regrid,atts_to_skip) |
---|
2177 | end if |
---|
2178 | if(.not.any("_FillValue".eq.atts_to_skip).and.isatt(sd,"_FillValue").and. \ |
---|
2179 | .not.isatt(sd_regrid,"_FillValue")) then |
---|
2180 | sd_regrid@_FillValue = sd@_FillValue |
---|
2181 | end if |
---|
2182 | end if |
---|
2183 | |
---|
2184 | ;---Create variable coords, unless requested otherwise (True by default) |
---|
2185 | if(.not.isatt_logical_false(opt,"CopyVarCoords").and.\ |
---|
2186 | .not.isatt_logical_false(opt,"CopyVarMeta")) then |
---|
2187 | ESMF_copy_varcoords(sd,sd_regrid,wgt_file_name,opt) |
---|
2188 | end if |
---|
2189 | end |
---|
2190 | |
---|
2191 | ;====================================================================== |
---|
2192 | ; This procedure calls the UNIX command "ESMF_RegridWeightGen". |
---|
2193 | ; |
---|
2194 | ; srcGridFile - the name of the NetCDF source grid file (ESMF or SCRIP) |
---|
2195 | ; dstGridFile - the name of the NetCDF destination grid file (ESMF or SCRIP) |
---|
2196 | ; wgtFile - the name of the NetCDF weight file to create. |
---|
2197 | ; opt - Set to True to indicate optional attributes are attached. |
---|
2198 | ; |
---|
2199 | ; The following attributes are allowed with "opt": |
---|
2200 | ; Debug - Turns on debug print statements |
---|
2201 | ; True or False (default=False) |
---|
2202 | ; SrcESMF - whether only the src grid file is an ESMF file |
---|
2203 | ; True or False (default=False) |
---|
2204 | ; DstESMF - whether only the dst grid file is an ESMF file |
---|
2205 | ; True or False (default=False) |
---|
2206 | ; SrcRegional - whether only the src grid file is regional |
---|
2207 | ; True or False (default=False) |
---|
2208 | ; DstRegional - whether only the dst grid file is regional |
---|
2209 | ; True or False (default=False) |
---|
2210 | ; IgnoreUnmappedPoints - whether to ignore unmapped points |
---|
2211 | ; True or False (default=True) |
---|
2212 | ; InterpMethod ("method" will work too) |
---|
2213 | ; "bilinear" (default), "patch" (slow), "conserve" |
---|
2214 | ; Pole ("pole" will work too) |
---|
2215 | ; "all" (default, unless InterpMethod=conserve), "none" |
---|
2216 | ; RemoveSrcFile |
---|
2217 | ; True or False (default=False) |
---|
2218 | ; Whether to remove srcGridFile after the |
---|
2219 | ; weight generation is done. |
---|
2220 | ; RemoveDstFile |
---|
2221 | ; True or False (default=False) |
---|
2222 | ; Whether to remove dstGridFile after the |
---|
2223 | ; weight generation is done. |
---|
2224 | ; RemoveFiles |
---|
2225 | ; True or False (default=False) |
---|
2226 | ; Whether to remove both src and dst grid files |
---|
2227 | ; after the weight generation is done. |
---|
2228 | ; |
---|
2229 | ; The ESMFBINDIR environment variable can be set prior to calling |
---|
2230 | ; this procedure, in order to indicate the location of this binary. |
---|
2231 | ; |
---|
2232 | ; The NumProc env var can be set to indicate the number of |
---|
2233 | ; processors, if the ESMF software was built with MPI turned on. |
---|
2234 | ; |
---|
2235 | ;====================================================================== |
---|
2236 | undef("ESMF_regrid_gen_weights") |
---|
2237 | procedure ESMF_regrid_gen_weights(srcGridFile[1]:string, dstGridFile[1]:string, \ |
---|
2238 | wgtFile[1]:string, opt[1]:logical) |
---|
2239 | local DEBUG, NumProc, Executer, ESMFCMD, ESMF_exec, interp_method, pole, opt |
---|
2240 | begin |
---|
2241 | valid_interp_methods = (/"bilinear","patch","conserve", \ |
---|
2242 | "nearestdtos","neareststod"/) |
---|
2243 | ; |
---|
2244 | ; Some attribute options cannot both be set if they have |
---|
2245 | ; different values. Check for them here. |
---|
2246 | ; |
---|
2247 | opt2 = opt |
---|
2248 | check_both_atts(opt2,"RemoveFiles","RemoveDstFile",False) |
---|
2249 | check_both_atts(opt2,"RemoveFiles","RemoveSrcFile",False) |
---|
2250 | check_both_atts(opt2,"WgtForceOverwrite","ForceOverwrite",False) |
---|
2251 | check_both_atts(opt2,"WgtOverwrite","Overwrite",False) |
---|
2252 | |
---|
2253 | if(isatt_logical_true(opt2,"RemoveDstFile")) |
---|
2254 | print("ESMF_regrid_gen_weights: Warning: RemoveDstFile was set to True.") |
---|
2255 | print(" This file might be needed later to generate coordinates.") |
---|
2256 | end if |
---|
2257 | |
---|
2258 | if(isatt_logical_true(opt2,"WgtOverwrite")) then |
---|
2259 | opt2@Overwrite = True |
---|
2260 | end if |
---|
2261 | |
---|
2262 | if(isatt_logical_true(opt2,"WgtForceOverwrite")) then |
---|
2263 | opt2@ForceOverwrite = True |
---|
2264 | end if |
---|
2265 | |
---|
2266 | ;---Name of ESMF offline regridder |
---|
2267 | ESMF_exec = "ESMF_RegridWeightGen" |
---|
2268 | |
---|
2269 | ;---Check for options |
---|
2270 | PrintTimings = isatt_logical_true(opt2,"PrintTimings") |
---|
2271 | if(PrintTimings) then |
---|
2272 | start_time = get_start_time() |
---|
2273 | end if |
---|
2274 | |
---|
2275 | DEBUG = isatt_logical_true(opt2,"Debug") |
---|
2276 | REMOVE_PET_LOG = isatt_logical_true(opt2,"RemovePETLog") |
---|
2277 | |
---|
2278 | ;---Check if the source grid file is a SCRIP file. |
---|
2279 | if (.not.(isatt_logical_true(opt2,"SrcESMF")).and. \ |
---|
2280 | .not.(is_SCRIP(srcGridFile,True))) then |
---|
2281 | print("ESMF_regrid_gen_weights: invalid or missing SCRIP source grid file.") |
---|
2282 | exit |
---|
2283 | end if |
---|
2284 | |
---|
2285 | ;---Check if the source grid file is an ESMF file. |
---|
2286 | if (isatt_logical_true(opt2,"SrcESMF").and. \ |
---|
2287 | .not.(is_ESMF(srcGridFile,True))) then |
---|
2288 | print("ESMF_regrid_gen_weights: invalid or missing ESMF source grid file.") |
---|
2289 | exit |
---|
2290 | end if |
---|
2291 | |
---|
2292 | ;---Check if the destination grid file is a SCRIP file. |
---|
2293 | if (.not.(isatt_logical_true(opt2,"DstESMF")).and. \ |
---|
2294 | .not.(is_SCRIP(dstGridFile,True))) then |
---|
2295 | print("ESMF_regrid_gen_weights: invalid or missing SCRIP destination grid file.") |
---|
2296 | exit |
---|
2297 | end if |
---|
2298 | |
---|
2299 | ;---Check if the destination grid file is a ESMF file. |
---|
2300 | if (isatt_logical_true(opt2,"DstESMF").and. \ |
---|
2301 | .not.(is_ESMF(dstGridFile,True))) then |
---|
2302 | print("ESMF_regrid_gen_weights: invalid or missing ESMF destination grid file.") |
---|
2303 | exit |
---|
2304 | end if |
---|
2305 | |
---|
2306 | ; |
---|
2307 | ; All seems ok. Run the ESMF regrid weight generator. |
---|
2308 | ; |
---|
2309 | ; Check the number of processor supported by the system. |
---|
2310 | ; |
---|
2311 | ; The user must have compiled ESMF with parallel features. |
---|
2312 | ; |
---|
2313 | if(ismissing(getenv("NumProc"))) then |
---|
2314 | NumProc = 1 |
---|
2315 | Executer = "" |
---|
2316 | else |
---|
2317 | NumProc = getenv("NumProc") |
---|
2318 | Executer = "mpirun -n " +NumProc+" " |
---|
2319 | end if |
---|
2320 | if(DEBUG) then |
---|
2321 | print("ESMF_regrid_gen_weights: number of processors used: " + NumProc) |
---|
2322 | end if |
---|
2323 | |
---|
2324 | ;---Check if the weight file already exists. |
---|
2325 | check_for_file(wgtFile,opt2) |
---|
2326 | |
---|
2327 | ;---Build the command (First Pass - non-optional features) |
---|
2328 | if (ismissing(getenv("ESMFBINDIR"))) then |
---|
2329 | ESMFCMD = Executer + ESMF_exec + " " \ |
---|
2330 | + "--source " + srcGridFile \ |
---|
2331 | + " --destination " + dstGridFile \ |
---|
2332 | + " --weight "+ wgtFile |
---|
2333 | else |
---|
2334 | ESMFCMD = Executer + "$ESMFBINDIR/" + ESMF_exec + " " \ |
---|
2335 | + "--source " + srcGridFile \ |
---|
2336 | + " --destination " + dstGridFile \ |
---|
2337 | + " --weight " + wgtFile |
---|
2338 | end if |
---|
2339 | |
---|
2340 | ;---Check for options |
---|
2341 | if (opt2) then |
---|
2342 | ; |
---|
2343 | ; Check if the user has requested a certain method. |
---|
2344 | ; Otherwise, the default will be used. |
---|
2345 | ; |
---|
2346 | if (isatt(opt2,"InterpMethod")) then |
---|
2347 | interp_method = str_lower(opt2@InterpMethod) |
---|
2348 | end if |
---|
2349 | if (isatt(opt2,"method")) then |
---|
2350 | interp_method = str_lower(opt2@method) |
---|
2351 | end if |
---|
2352 | if (isvar("interp_method")) then |
---|
2353 | if(.not.any(interp_method.eq.valid_interp_methods)) then |
---|
2354 | print("ESMF_regrid_gen_weights: InterpMethod must be one of " + \ |
---|
2355 | str_join(valid_interp_methods,", ")) |
---|
2356 | exit |
---|
2357 | end if |
---|
2358 | ESMFCMD = ESMFCMD + " --method " + interp_method |
---|
2359 | end if |
---|
2360 | ; |
---|
2361 | ; Check if the user has specified how to handle the poles |
---|
2362 | ; Otherwise, the default will be used. |
---|
2363 | ; |
---|
2364 | if (isatt(opt2,"Pole")) then |
---|
2365 | pole = str_lower(opt2@Pole) |
---|
2366 | end if |
---|
2367 | if (isatt(opt2,"pole")) then |
---|
2368 | pole = str_lower(opt2@pole) |
---|
2369 | end if |
---|
2370 | if(isvar("interp_method").and.isvar("pole").and.pole.eq."all".and.\ |
---|
2371 | any(interp_method.eq.(/"conserve","neareststod","nearestdtos"/))) then |
---|
2372 | print("ESMF_regrid_gen_weights: warning: 'Pole' cannot be set to 'all' when") |
---|
2373 | print(" using 'conserve' method. Defaulting to 'none'.") |
---|
2374 | pole = "none" |
---|
2375 | end if |
---|
2376 | |
---|
2377 | if (isvar("pole")) then |
---|
2378 | ESMFCMD = ESMFCMD + " --pole " + pole |
---|
2379 | end if |
---|
2380 | |
---|
2381 | ;---Check if the user specifies that source grid is regional |
---|
2382 | if (isatt_logical_true(opt2,"SrcRegional")) then |
---|
2383 | ESMFCMD = ESMFCMD + " --src_regional" |
---|
2384 | end if |
---|
2385 | |
---|
2386 | ;---Check if the user specifies that destination grid is regional |
---|
2387 | if (isatt_logical_true(opt2,"DstRegional")) then |
---|
2388 | ESMFCMD = ESMFCMD + " --dst_regional" |
---|
2389 | end if |
---|
2390 | |
---|
2391 | ;---Checking if the user specifies that source grid is ESMF |
---|
2392 | if (isatt_logical_true(opt2,"SrcESMF")) then |
---|
2393 | ESMFCMD = ESMFCMD + " --src_type ESMF" |
---|
2394 | end if |
---|
2395 | |
---|
2396 | ;---Check if the user specifies that destination grid is ESMF |
---|
2397 | if (isatt_logical_true(opt2,"DstESMF")) then |
---|
2398 | ESMFCMD = ESMFCMD + " --dst_type ESMF" |
---|
2399 | end if |
---|
2400 | |
---|
2401 | ;---This flag is necessary for V5.2.0r1 and grids like WRF. |
---|
2402 | if (.not.isatt_logical_false(opt2,"IgnoreUnmappedPoints")) then |
---|
2403 | ESMFCMD = ESMFCMD + " -i" |
---|
2404 | end if |
---|
2405 | |
---|
2406 | ;---This flag is necessary for writing vars > 2 GB |
---|
2407 | if (isatt_logical_true(opt2,"LargeFile")) then |
---|
2408 | ESMFCMD = ESMFCMD + " --64bit_offset" |
---|
2409 | end if |
---|
2410 | |
---|
2411 | ;---Check if the user has requested to print out the full command to be run. |
---|
2412 | end if ; end of building the command (Second Pass - optional features) |
---|
2413 | |
---|
2414 | if (DEBUG) |
---|
2415 | print("--------------------------------------------------") |
---|
2416 | print("ESMF_regrid_gen_weights: the following command is about to be executed on the system:") |
---|
2417 | print("'"+ESMFCMD+"'") |
---|
2418 | print("--------------------------------------------------") |
---|
2419 | end if |
---|
2420 | |
---|
2421 | ;---Execute the regridding command |
---|
2422 | RegridOut = systemfunc(ESMFCMD) |
---|
2423 | |
---|
2424 | if(REMOVE_PET_LOG) then |
---|
2425 | system("rm -rf PET0.RegridWeightGen.Log") |
---|
2426 | end if |
---|
2427 | |
---|
2428 | if(DEBUG) then |
---|
2429 | print("ESMF_regrid_gen_weights: output from '" + ESMF_exec + "':") |
---|
2430 | print(" " + RegridOut) |
---|
2431 | print("--------------------------------------------------") |
---|
2432 | end if |
---|
2433 | ; |
---|
2434 | ; The binary tool performs more sophisticated tests. |
---|
2435 | ; Here it is checked that everything went ok. |
---|
2436 | ; |
---|
2437 | if (.not.(any(str_squeeze(RegridOut).eq. \ |
---|
2438 | "Completed weight generation successfully."))) then |
---|
2439 | print("ESMF_regrid_gen_weights: '" + ESMF_exec + "' was not successful.") |
---|
2440 | exit |
---|
2441 | end if |
---|
2442 | if(DEBUG) then |
---|
2443 | print("ESMF_regrid_gen_weights: '" + ESMF_exec + "' was successful.") |
---|
2444 | end if |
---|
2445 | |
---|
2446 | ; |
---|
2447 | ; Clean up, if desired. We don't allow for the removal of the weight |
---|
2448 | ; file here because it might be needed later to generate coordinate |
---|
2449 | ; information. We might decide to change this. |
---|
2450 | ; |
---|
2451 | if (isatt_logical_true(opt2,"RemoveFiles").or. \ |
---|
2452 | isatt_logical_true(opt2,"RemoveSrcFile")) then |
---|
2453 | if(DEBUG) then |
---|
2454 | print("ESMF_regrid_gen_weights: By request, removing '" + srcGridFile + "'") |
---|
2455 | end if |
---|
2456 | system("rm -rf " + srcGridFile) |
---|
2457 | end if |
---|
2458 | |
---|
2459 | if (isatt_logical_true(opt2,"RemoveFiles").or. \ |
---|
2460 | isatt_logical_true(opt2,"RemoveDstFile")) then |
---|
2461 | if(DEBUG) then |
---|
2462 | print("ESMF_regrid_gen_weights: By request, removing '" + dstGridFile + "'") |
---|
2463 | end if |
---|
2464 | system("rm -rf " + dstGridFile) |
---|
2465 | end if |
---|
2466 | |
---|
2467 | if(PrintTimings) then |
---|
2468 | print_elapsed_time(start_time,"ESMF_regrid_gen_weights") |
---|
2469 | end if |
---|
2470 | |
---|
2471 | end ; of ESMF_regrid_gen_weights(...) |
---|
2472 | |
---|
2473 | ;====================================================================== |
---|
2474 | ; Using the provided weight file, remaps the data |
---|
2475 | ; from source grid into destination grid |
---|
2476 | ; |
---|
2477 | ; I've been tempted to add options for removing the source, destination, |
---|
2478 | ; and weight files after this routine is done. But, ESMF_regrid can |
---|
2479 | ; do this, so it's easier to let this routine "do it all" than to |
---|
2480 | ; have the individual functions do it. |
---|
2481 | ;====================================================================== |
---|
2482 | undef("ESMF_regrid_with_weights") |
---|
2483 | function ESMF_regrid_with_weights(srcData:numeric,wgtFile[1]:string, opt[1]:logical) |
---|
2484 | local DEBUG, fmsg, fid, srclat, srclon, dstlat, dstlon, src_grid_dims, \ |
---|
2485 | src_grid_rank, srcData_dims, srcData_rank, srcData_rgt_dims, \ |
---|
2486 | srcData_lft_dims, has_leftmost_dims, dst_grid_dims, dst_grid_rank, \ |
---|
2487 | dst_grid_dims_in, row, col, v, x, minDataValue, maxDataValue, \ |
---|
2488 | RemapIndexes, Indexes, IndexesDims, dstData_tmp2d |
---|
2489 | begin |
---|
2490 | fmsg = new(1,float) ; For error return |
---|
2491 | |
---|
2492 | ;---Check for options |
---|
2493 | PrintTimings = isatt_logical_true(opt,"PrintTimings") |
---|
2494 | DEBUG = isatt_logical_true(opt,"Debug") |
---|
2495 | ; |
---|
2496 | ; If the input type is float, and the user hasn't explicitly set |
---|
2497 | ; ReturnDouble to True, then floats will be returned. |
---|
2498 | ; |
---|
2499 | if(typeof(srcData).ne."double".and.\ |
---|
2500 | .not.isatt_logical_true(opt,"ReturnDouble")) then |
---|
2501 | ReturnFloat = True |
---|
2502 | else |
---|
2503 | ReturnFloat = False |
---|
2504 | end if |
---|
2505 | |
---|
2506 | if(PrintTimings) then |
---|
2507 | start_time = get_start_time() |
---|
2508 | end if |
---|
2509 | |
---|
2510 | ; |
---|
2511 | ; Check if the indexes on the file actually represent indexes |
---|
2512 | ; into a larger 2D grid. If so, we have get the Indexes and |
---|
2513 | ; the dimension sizes of the larger 2D grid, for remapping |
---|
2514 | ; later. |
---|
2515 | ; |
---|
2516 | RemapIndexes = isatt_logical_true(opt,"RemapIndexes") |
---|
2517 | if(RemapIndexes) then |
---|
2518 | if(isatt(opt,"Indexes")) then |
---|
2519 | Indexes = opt@Indexes |
---|
2520 | else |
---|
2521 | print("ESMF_regrid_with_weights: error: you set RemapIndexes=True, but didn't provide 'Indexes'.") |
---|
2522 | exit |
---|
2523 | end if |
---|
2524 | if(isatt(opt,"IndexesDims")) then |
---|
2525 | IndexesDims = opt@IndexesDims |
---|
2526 | else |
---|
2527 | print("ESMF_regrid_with_weights: error: you set RemapIndexes=True, but didn't provide the ") |
---|
2528 | print(" dimension sizes of the nD array (IndexesDims") |
---|
2529 | exit |
---|
2530 | end if |
---|
2531 | end if |
---|
2532 | |
---|
2533 | if(DEBUG) then |
---|
2534 | print("ESMF_regrid_with_weights: regridding using interpolation weights ...") |
---|
2535 | end if |
---|
2536 | |
---|
2537 | ;---Make sure the weight file is accessible |
---|
2538 | map_method = "unknown" |
---|
2539 | if (.not.(isfilepresent(wgtFile))) then |
---|
2540 | print("ESMF_regrid_with_weights: cannot open weight file '" + \ |
---|
2541 | wgtFile + "'.") |
---|
2542 | return(fmsg) |
---|
2543 | else |
---|
2544 | fid = addfile(wgtFile,"r") |
---|
2545 | if(isatt(fid,"map_method")) then |
---|
2546 | map_method = fid@map_method |
---|
2547 | end if |
---|
2548 | ;---Error checking |
---|
2549 | ns = getfilevardimsizes(fid,"S") |
---|
2550 | if(all(ismissing(ns)).or.all(ns.eq.0)) then |
---|
2551 | print("ESMF_regrid_with_weights: error: there are no weights on this file.") |
---|
2552 | print(" This likely means there was a problem with the mapping") |
---|
2553 | print(" from the source grid to the destination grid.") |
---|
2554 | print(" Check that both grids are valid.") |
---|
2555 | return(fmsg) |
---|
2556 | end if |
---|
2557 | end if |
---|
2558 | |
---|
2559 | ; |
---|
2560 | ; Apply frac_b only for conservative interpolation. We may |
---|
2561 | ; change this later to let the user decide when to apply. |
---|
2562 | ; The ESMF folks said that it was only needed for the |
---|
2563 | ; "conserve" case, and they reconfirmed this Oct 2012. |
---|
2564 | ; |
---|
2565 | if(map_method.eq."Conservative remapping") then |
---|
2566 | ApplyFrac_B = True |
---|
2567 | frac_b = fid->frac_b |
---|
2568 | frac_b@_FillValue = default_fillvalue(typeof(frac_b)) |
---|
2569 | frac_b = where(frac_b.eq.0,frac_b@_FillValue,frac_b) |
---|
2570 | else |
---|
2571 | ApplyFrac_B = False |
---|
2572 | end if |
---|
2573 | |
---|
2574 | ;---Get the lat/lon of source and destination grids |
---|
2575 | srclat = fid->yc_a |
---|
2576 | srclon = fid->xc_a |
---|
2577 | dstlat = fid->yc_b |
---|
2578 | dstlon = fid->xc_b |
---|
2579 | |
---|
2580 | ;---Check that the source grid "covers" the destination grid |
---|
2581 | if ( DEBUG.and..not.( \ |
---|
2582 | (min(dstlat).ge.min(srclat)).and. \ |
---|
2583 | (max(dstlat).le.max(srclat)).and. \ |
---|
2584 | (min(dstlon).ge.min(srclon)).and. \ |
---|
2585 | (max(dstlon).le.max(srclon)) ) ) then |
---|
2586 | print("ESMF_regrid_with_weights: warning: destination grid is not") |
---|
2587 | print(" completely covered by the source grid. This is not an error.") |
---|
2588 | print(" It just means your destination grid covers a larger area") |
---|
2589 | print(" than your source grid.") |
---|
2590 | end if |
---|
2591 | |
---|
2592 | ;---Get the dimensions and rank of the source grid and input grid. |
---|
2593 | src_grid_dims = fid->src_grid_dims(::-1) ; dimensions are Fortran-based |
---|
2594 | src_grid_rank = dimsizes(src_grid_dims) |
---|
2595 | is_src_grid_scalar = src_grid_rank.eq.1.and.src_grid_dims(0).eq.1 |
---|
2596 | |
---|
2597 | srcData_dims = dimsizes(srcData) |
---|
2598 | srcData_rank = dimsizes(srcData_dims) |
---|
2599 | |
---|
2600 | ;---Get the dimensions and rank of the destination grid |
---|
2601 | dst_grid_dims = fid->dst_grid_dims(::-1) ; dimensions are Fortran based |
---|
2602 | dst_grid_rank = dimsizes(dst_grid_dims) |
---|
2603 | |
---|
2604 | ;---Check dimensions of srcData |
---|
2605 | if(src_grid_rank.gt.srcData_rank) then |
---|
2606 | print("ESMF_regrid_with_weights: error: the rank of the source dimensions on the") |
---|
2607 | print(" weight file is more than the rank of the data") |
---|
2608 | print(" to be regridded. Can't continue.") |
---|
2609 | return(fmsg) |
---|
2610 | end if |
---|
2611 | |
---|
2612 | if(DEBUG) then |
---|
2613 | ;---Source grid info |
---|
2614 | print("ESMF_regrid_with_weights: Source Grid:") |
---|
2615 | print(" rank: " + src_grid_rank) |
---|
2616 | str = " dimensions:" |
---|
2617 | do i=0,src_grid_rank-1 |
---|
2618 | str = str + " " + src_grid_dims(i) |
---|
2619 | end do |
---|
2620 | print("" + str) |
---|
2621 | print(" original source rank: " + srcData_rank) |
---|
2622 | print(" latitude min/max: " + min(srclat) + "/" + max(srclat)) |
---|
2623 | print(" longitude min/max:" + min(srclon) + "/" + max(srclon)) |
---|
2624 | ;---Destination grid info |
---|
2625 | print("ESMF_regrid_with_weights: Destination Grid:") |
---|
2626 | str = " dimensions:" |
---|
2627 | do i=0,dst_grid_rank-1 |
---|
2628 | str = str + " " + dst_grid_dims(i) |
---|
2629 | end do |
---|
2630 | print("" + str) |
---|
2631 | print(" latitude min/max: " + min(dstlat) + "/" + max(dstlat)) |
---|
2632 | print(" longitude min/max:" + min(dstlon) + "/" + max(dstlon)) |
---|
2633 | end if |
---|
2634 | |
---|
2635 | ; |
---|
2636 | ; The source dimensions on the description file will not |
---|
2637 | ; necessarily be the same as the original source dimensions |
---|
2638 | ; of the grid you want to regrid from. You have to take |
---|
2639 | ; that into account in this whole procedure, and make sure |
---|
2640 | ; you use the correct dimensions depending on what you're |
---|
2641 | ; doing. |
---|
2642 | ; |
---|
2643 | if(.not.is_src_grid_scalar.and.srcData_rank.gt.src_grid_rank) then |
---|
2644 | srcData_lft_ndims = srcData_rank - src_grid_rank |
---|
2645 | srcData_rgt_dims = srcData_dims(srcData_lft_ndims:) |
---|
2646 | srcData_lft_dims = srcData_dims(0:srcData_lft_ndims-1) |
---|
2647 | else |
---|
2648 | srcData_rgt_dims = srcData_dims |
---|
2649 | srcData_lft_ndims = 0 |
---|
2650 | end if |
---|
2651 | |
---|
2652 | ; |
---|
2653 | ; Retrieve the interpolation weights. They are stored as a |
---|
2654 | ; sparse matrix in a coordinate list format |
---|
2655 | ; |
---|
2656 | if(DEBUG) then |
---|
2657 | print("ESMF_regrid_with_weights: retrieving interpolation weights ...") |
---|
2658 | end if |
---|
2659 | |
---|
2660 | row = fid->row |
---|
2661 | col = fid->col |
---|
2662 | v = fid->S |
---|
2663 | |
---|
2664 | ;---Check that the column indexes will map into src_grid_dims |
---|
2665 | if ( max(col).gt.product(srcData_rgt_dims) ) then |
---|
2666 | print("ESMF_regrid_with_weights: error: source data on the description") |
---|
2667 | print(" file does not have proper dimensions.") |
---|
2668 | return(fmsg) |
---|
2669 | end if |
---|
2670 | |
---|
2671 | ; |
---|
2672 | ; We have to convert the src grid to 1D no matter what. |
---|
2673 | ; However, we have to determine if there are any leftmost |
---|
2674 | ; dimensions to account for. |
---|
2675 | ; |
---|
2676 | if(.not.is_src_grid_scalar.and.srcData_rank.gt.src_grid_rank) then |
---|
2677 | src_grid_dims_tmp = new(srcData_lft_ndims+1,long) |
---|
2678 | src_grid_dims_tmp(0:srcData_lft_ndims-1) = srcData_lft_dims |
---|
2679 | src_grid_dims_tmp(srcData_lft_ndims) = product(srcData_rgt_dims) |
---|
2680 | x = reshape(srcData,src_grid_dims_tmp) |
---|
2681 | else |
---|
2682 | x = reshape(srcData,product(srcData_dims)) |
---|
2683 | end if |
---|
2684 | ; |
---|
2685 | ; Note from Mohammad: |
---|
2686 | ; Currently there is a problem with ESMF files. |
---|
2687 | ; once it is fixed by ESMF people This can be reinstated. |
---|
2688 | ; if( max(row).gt.product(dst_grid_dims) ) then |
---|
2689 | ; print("ESMF_regrid_with_weights: error: weight file has internal mistmatched data or is corrupted.") |
---|
2690 | ; return(fmsg) |
---|
2691 | ; end if |
---|
2692 | |
---|
2693 | if(DEBUG) |
---|
2694 | print("ESMF_regrid_with_weights: calling sparse_matrix_mult to apply weights...") |
---|
2695 | end if |
---|
2696 | |
---|
2697 | ; |
---|
2698 | ; We have to always treat the output grid as a 1D array |
---|
2699 | ; of length N because ESMF gives us the indices into a |
---|
2700 | ; 1D array. |
---|
2701 | ; |
---|
2702 | ; If dst_grid_dims is 1, then we have to set N=max(row). |
---|
2703 | ; |
---|
2704 | if(dst_grid_rank.eq.1) then |
---|
2705 | dst_grid_dims_in = max(row) |
---|
2706 | else |
---|
2707 | dst_grid_dims_in = product(dst_grid_dims) |
---|
2708 | end if |
---|
2709 | dst_grid_dims_in = product(dst_grid_dims) |
---|
2710 | ; |
---|
2711 | ; We will need to reshape the return (M) x N grid from |
---|
2712 | ; sparse_matrix_mult if the output dimensions are not 1D. |
---|
2713 | ; |
---|
2714 | ; We also need to divide by frac_b if using the "conserve" |
---|
2715 | ; method. We are trying to avoid making a separate copy |
---|
2716 | ; of the array, if float return is desired. So, we have |
---|
2717 | ; 8 special "if" tests to avoid this as much as possible: |
---|
2718 | ; |
---|
2719 | ; Return array has to be reshaped and converted to float |
---|
2720 | ; Case 1: Apply frac_b |
---|
2721 | ; Case 2: Don't apply frac_b |
---|
2722 | ; Return array has to be reshaped, but leave as double |
---|
2723 | ; Case 3: Apply frac_b |
---|
2724 | ; Case 4: Don't apply frac_b |
---|
2725 | ; Return array has to be converted to float (no reshaping) |
---|
2726 | ; Case 5: Apply frac_b |
---|
2727 | ; Case 6: Don't apply frac_b |
---|
2728 | ; Return array left as double (no reshaping) |
---|
2729 | ; Case 7: Apply frac_b, convert to float |
---|
2730 | ; Case 8: Convert to float |
---|
2731 | ; |
---|
2732 | if(ApplyFrac_B.and.DEBUG) then |
---|
2733 | print("ESMF_regrid_with_weights: dividing results by frac_b...") |
---|
2734 | print(" (currently for conserve interpolation only)") |
---|
2735 | end if |
---|
2736 | |
---|
2737 | if(dst_grid_rank.gt.1) then |
---|
2738 | dst_grid_dims_out = new(srcData_lft_ndims+dst_grid_rank,long) |
---|
2739 | if(.not.is_src_grid_scalar.and.srcData_rank.gt.src_grid_rank) then |
---|
2740 | dst_grid_dims_out(0:srcData_lft_ndims-1) = srcData_lft_dims |
---|
2741 | end if |
---|
2742 | dst_grid_dims_out(srcData_lft_ndims:) = dst_grid_dims |
---|
2743 | if(ReturnFloat) then |
---|
2744 | ;---Case 1: Apply frac_b, reshape, convert to float |
---|
2745 | if(ApplyFrac_B) then |
---|
2746 | dstData = tofloat(reshape(sparse_matrix_mult(row-1,col-1,v,x,\ |
---|
2747 | dst_grid_dims_in),dst_grid_dims_out) / \ |
---|
2748 | reshape(frac_b,dst_grid_dims_out)) |
---|
2749 | else |
---|
2750 | ;---Case 2: Reshape, convert to float |
---|
2751 | dstData = tofloat(reshape(sparse_matrix_mult(row-1,col-1,v,x,\ |
---|
2752 | dst_grid_dims_in),dst_grid_dims_out)) |
---|
2753 | end if |
---|
2754 | else |
---|
2755 | ;---Case 3: Apply frac_b, reshape, leave as double |
---|
2756 | if(ApplyFrac_B) then |
---|
2757 | dstData = reshape(sparse_matrix_mult(row-1,col-1,v,x,\ |
---|
2758 | dst_grid_dims_in),dst_grid_dims_out) / \ |
---|
2759 | reshape(frac_b,dst_grid_dims_out) |
---|
2760 | else |
---|
2761 | ;---Case 4: Reshape, leave as double |
---|
2762 | dstData = reshape(sparse_matrix_mult(row-1,col-1,v,x,\ |
---|
2763 | dst_grid_dims_in),dst_grid_dims_out) |
---|
2764 | end if |
---|
2765 | end if |
---|
2766 | else |
---|
2767 | if(ReturnFloat) then |
---|
2768 | ;---Case 5: Apply frac_b, convert to float (no reshape) |
---|
2769 | if(ApplyFrac_B) then |
---|
2770 | dstData = tofloat(sparse_matrix_mult(row-1,col-1,v,x,\ |
---|
2771 | dst_grid_dims_in)/frac_b) |
---|
2772 | else |
---|
2773 | ;---Case 6: Convert to float (no reshape) |
---|
2774 | dstData = tofloat(sparse_matrix_mult(row-1,col-1,v,x,dst_grid_dims_in)) |
---|
2775 | end if |
---|
2776 | else |
---|
2777 | ;---Case 7: Apply frac_b, leave as double (no reshape) |
---|
2778 | if(ApplyFrac_B) then |
---|
2779 | dstData = sparse_matrix_mult(row-1,col-1,v,x,dst_grid_dims_in)/frac_b |
---|
2780 | else |
---|
2781 | ;---Case 8: Leave as double (no reshape) |
---|
2782 | dstData = sparse_matrix_mult(row-1,col-1,v,x,dst_grid_dims_in) |
---|
2783 | end if |
---|
2784 | end if |
---|
2785 | end if |
---|
2786 | |
---|
2787 | ;---Clean up to save memory |
---|
2788 | if(ApplyFrac_B) then |
---|
2789 | delete(frac_b) |
---|
2790 | end if |
---|
2791 | |
---|
2792 | ;---Make sure dstData has a missing value |
---|
2793 | if(isatt(srcData,"_FillValue")) then |
---|
2794 | dstData@_FillValue = totypeof(srcData@_FillValue,typeof(dstData)) |
---|
2795 | else |
---|
2796 | dstData@_FillValue = default_fillvalue(typeof(dstData)) |
---|
2797 | end if |
---|
2798 | |
---|
2799 | ;---Check if we need to remap the values onto a larger 2D grid. |
---|
2800 | if(RemapIndexes) then |
---|
2801 | if(DEBUG) |
---|
2802 | print("ESMF_regrid_with_weights: putting interpolated values back onto larger 2D grid...") |
---|
2803 | end if |
---|
2804 | dstData_tmp2d = reshape_ind(dstData,Indexes,IndexesDims) |
---|
2805 | delete(dstData) |
---|
2806 | dstData = dstData_tmp2d |
---|
2807 | end if |
---|
2808 | |
---|
2809 | if(DEBUG) then |
---|
2810 | print("ESMF_regrid_with_weights: dstData") |
---|
2811 | str = " Dimensions:" |
---|
2812 | nd = dimsizes(dstData) |
---|
2813 | do i=0,dimsizes(nd)-1 |
---|
2814 | str = str + " " + nd(i) |
---|
2815 | end do |
---|
2816 | print("" + str) |
---|
2817 | print(" minSrcData: " + min(srcData)) |
---|
2818 | print(" maxSrcData: " + max(srcData)) |
---|
2819 | print(" minDstData: " + min(dstData)) |
---|
2820 | print(" maxDstData: " + max(dstData)) |
---|
2821 | end if |
---|
2822 | |
---|
2823 | ;---Copy variable attributes and coordinate arrays if requested. |
---|
2824 | delete(fid) ; close weight file before it gets opened again |
---|
2825 | ESMF_copy_varmeta(srcData,dstData,wgtFile,opt) |
---|
2826 | |
---|
2827 | ;---------------------------------------------------------------------- |
---|
2828 | ; Clean up, add, and remove attributes. |
---|
2829 | ; |
---|
2830 | ; Note that due to standards for the weights file, the map_method |
---|
2831 | ; attribute will say "Bilinear remapping" for both bilinear and patch |
---|
2832 | ; methods. If this routine is called by ESMF_regrid, then that routine |
---|
2833 | ; will set FixMapMethodForPatch to True so this routine knows to fix |
---|
2834 | ; the "remap" attribute. Otherwise if ESMF_regrid_with_weights is |
---|
2835 | ; called directly, there's no way to tell if patch or bilinear was |
---|
2836 | ; used. |
---|
2837 | ;---------------------------------------------------------------------- |
---|
2838 | if (.not.isatt(dstData,"remap")) then |
---|
2839 | if(isatt_logical_true(opt,"FixMapMethodForPatch")) then |
---|
2840 | dstData@remap = "remapped via ESMF_regrid_with_weights: " + \ |
---|
2841 | str_sub_str(map_method,"Bilinear","Patch") |
---|
2842 | else |
---|
2843 | dstData@remap = "remapped via ESMF_regrid_with_weights: " + \ |
---|
2844 | map_method |
---|
2845 | end if |
---|
2846 | end if |
---|
2847 | |
---|
2848 | ;---Check for missing value attributes and fix them if necessary |
---|
2849 | if(.not.isatt(dstData,"missing_value")) then |
---|
2850 | dstData@missing_value = dstData@_FillValue |
---|
2851 | else |
---|
2852 | if(typeof(dstData@missing_value).ne.typeof(dstData)) then |
---|
2853 | tmp_msg = totypeof(dstData@missing_value,typeof(dstData)) |
---|
2854 | delete(dstData@missing_value) |
---|
2855 | dstData@missing_value = tmp_msg |
---|
2856 | end if |
---|
2857 | end if |
---|
2858 | |
---|
2859 | if(isatt(dstData,"_FillValue_original")) then |
---|
2860 | delete(dstData@_FillValue_original) |
---|
2861 | end if |
---|
2862 | if(isatt(dstData,"missing_value_original")) then |
---|
2863 | delete(dstData@missing_value_original) |
---|
2864 | end if |
---|
2865 | |
---|
2866 | if(PrintTimings) then |
---|
2867 | print_elapsed_time(start_time,"ESMF_regrid_with_weights") |
---|
2868 | end if |
---|
2869 | |
---|
2870 | return(dstData) |
---|
2871 | end ; of ESMF_regrid_with_weights(...) |
---|
2872 | |
---|
2873 | ;---------------------------------------------------------------------- |
---|
2874 | ; Note: this routine should be combined with "get_dst_grid_info" to |
---|
2875 | ; make just one "get_grid_info" routine. For now, any changes made to |
---|
2876 | ; this routine should potentially be done to "get_dst_grid_info". |
---|
2877 | ; |
---|
2878 | ; Given a source data variable, this function returns information |
---|
2879 | ; about the source grid, and the lat/lon values, if appropriate. |
---|
2880 | ; |
---|
2881 | ; The lat/lon values can be provided via: |
---|
2882 | ; |
---|
2883 | ; - SrcGridLat/SrcGridLon |
---|
2884 | ; |
---|
2885 | ; - Coordinate arrays [rectilinear grid] |
---|
2886 | ; |
---|
2887 | ; - lat2d/lon2d attributes [curvilinear grid] |
---|
2888 | ; |
---|
2889 | ; - lat1d/lon1d attributes [unstructured grid] |
---|
2890 | ; |
---|
2891 | ; - By a grid type of "5deg", "1x1", "2x3", etc. |
---|
2892 | ;---------------------------------------------------------------------- |
---|
2893 | undef("get_src_grid_info") |
---|
2894 | function get_src_grid_info(opt,data) |
---|
2895 | local opt2, grid_type_name, grid_lat_name, grid_lon_name, islatlon, \ |
---|
2896 | grid_string, lat_vals, lon_vals, lat_dims, lon_dims, slat_dims, slon_dims, \ |
---|
2897 | lat_rank, lon_rank, var_dims, var_rank, left_dims, grid_long_string |
---|
2898 | begin |
---|
2899 | opt2 = opt |
---|
2900 | grid_string = "Src" |
---|
2901 | grid_long_string = "source" |
---|
2902 | |
---|
2903 | DEBUG = isatt_logical_true(opt2,"Debug") |
---|
2904 | |
---|
2905 | var_dims = dimsizes(data) |
---|
2906 | var_rank = dimsizes(var_dims) |
---|
2907 | |
---|
2908 | ;---List of some of the possible attributes attached to "opt". |
---|
2909 | grid_type_name = grid_string + "GridType" |
---|
2910 | grid_lat_name = grid_string + "GridLat" |
---|
2911 | grid_lon_name = grid_string + "GridLon" |
---|
2912 | |
---|
2913 | grid_type = str_lower(get_att_value(opt2,grid_type_name,"unknown")) |
---|
2914 | |
---|
2915 | ;---Check if SrcGridLat/SrcGridLon were specified. |
---|
2916 | if(isatt(opt2,grid_lat_name).and.isatt(opt2,grid_lon_name)) then |
---|
2917 | islatlon = True |
---|
2918 | lat_vals = opt2@$grid_lat_name$ |
---|
2919 | lon_vals = opt2@$grid_lon_name$ |
---|
2920 | |
---|
2921 | ;---Curvilinear grid? |
---|
2922 | else if(isatt(data,"lat2d").and.isatt(data,"lon2d")) then |
---|
2923 | if(.not.any(grid_type.eq.(/"unknown","curvilinear"/))) then |
---|
2924 | print("get_src_grid_info: the lat/lon values provided don't match") |
---|
2925 | print(" the grid type (" + grid_type + ") specified.") |
---|
2926 | exit |
---|
2927 | end if |
---|
2928 | |
---|
2929 | grid_type = "curvilinear" |
---|
2930 | islatlon = True |
---|
2931 | lat_vals = data@lat2d |
---|
2932 | lon_vals = data@lon2d |
---|
2933 | |
---|
2934 | ;---Rectilinear grid? |
---|
2935 | else if(var_rank.ge.2.and. \ |
---|
2936 | isdimnamed(data,var_rank-1).and. \ |
---|
2937 | isdimnamed(data,var_rank-2).and. \ |
---|
2938 | iscoord(data,data!(var_rank-1)).and. \ |
---|
2939 | iscoord(data,data!(var_rank-2))) then |
---|
2940 | |
---|
2941 | if(.not.any(grid_type.eq.(/"unknown","rectilinear"/))) then |
---|
2942 | print("get_src_grid_info: the lat/lon values provided don't match") |
---|
2943 | print(" the grid type (" + grid_type + ")") |
---|
2944 | exit |
---|
2945 | end if |
---|
2946 | |
---|
2947 | grid_type = "rectilinear" |
---|
2948 | islatlon = True |
---|
2949 | lat_vals = data&$data!(var_rank-2)$ |
---|
2950 | lon_vals = data&$data!(var_rank-1)$ |
---|
2951 | ;---Unstructured grid? |
---|
2952 | else if(isatt(data,"lat1d").and.isatt(data,"lon1d")) then |
---|
2953 | if(.not.any(grid_type.eq.(/"unknown","unstructured"/))) then |
---|
2954 | print("get_src_grid_info: the lat/lon values provided don't match") |
---|
2955 | print(" the grid type (" + grid_type + \ |
---|
2956 | ") specified.") |
---|
2957 | exit |
---|
2958 | end if |
---|
2959 | |
---|
2960 | grid_type = "unstructured" |
---|
2961 | islatlon = True |
---|
2962 | lat_vals = data@lat1d |
---|
2963 | lon_vals = data@lon1d |
---|
2964 | |
---|
2965 | ;---Better be a grid like "5deg" or "2x3"! |
---|
2966 | else |
---|
2967 | islatlon = False ; No lat/lon values specified |
---|
2968 | end if |
---|
2969 | end if |
---|
2970 | end if |
---|
2971 | end if |
---|
2972 | |
---|
2973 | if(grid_type.eq."unknown".and..not.islatlon) then |
---|
2974 | print("get_src_grid_info: can't determine what type of " + \ |
---|
2975 | grid_long_string + " grid you have.") |
---|
2976 | exit |
---|
2977 | end if |
---|
2978 | |
---|
2979 | ;---rectilinear, curvilinear, and unstructured require lat/lon values. |
---|
2980 | if(any(grid_type.eq.(/"rectilinear","curvilinear","unstructured"/)).and.\ |
---|
2981 | .not.islatlon) then |
---|
2982 | print("get_src_grid_info: no lat/lon values provided for type of grid specified (" + grid_type + ").") |
---|
2983 | exit |
---|
2984 | end if |
---|
2985 | |
---|
2986 | if(grid_type.eq."unknown".and..not.islatlon) then |
---|
2987 | print("get_src_grid_info: can't determine type of grid you have.") |
---|
2988 | exit |
---|
2989 | end if |
---|
2990 | if(islatlon) then |
---|
2991 | lat_dims = dimsizes(lat_vals) |
---|
2992 | lon_dims = dimsizes(lon_vals) |
---|
2993 | lat_rank = dimsizes(lat_dims) |
---|
2994 | lon_rank = dimsizes(lon_dims) |
---|
2995 | slat_dims = tostring(lat_dims) |
---|
2996 | slon_dims = tostring(lon_dims) |
---|
2997 | |
---|
2998 | if(DEBUG) then |
---|
2999 | print("get_src_grid_info: " + grid_long_string + \ |
---|
3000 | " lat dims = (" + str_join(slat_dims,",") + ")") |
---|
3001 | print("get_src_grid_info: " + grid_long_string + \ |
---|
3002 | " lon dims = (" + str_join(slon_dims,",") + ")") |
---|
3003 | end if |
---|
3004 | |
---|
3005 | if(lat_rank.gt.2.or.lon_rank.gt.2.or.lat_rank.ne.lon_rank) then |
---|
3006 | print("get_src_grid_info: invalid " + grid_long_string + \ |
---|
3007 | " lat/lon given.") |
---|
3008 | exit |
---|
3009 | end if |
---|
3010 | |
---|
3011 | ;---If grid_type still unknown at this point, try to guess at it. |
---|
3012 | if(grid_type.eq."unknown") then |
---|
3013 | ;---Test for rectilinear |
---|
3014 | if(lat_rank.eq.1.and.var_rank.gt.1.and.\ |
---|
3015 | var_dims(var_rank-2).eq.lat_dims.and.\ |
---|
3016 | var_dims(var_rank-1).eq.lon_dims) then |
---|
3017 | grid_type = "rectilinear" |
---|
3018 | ;---Test for curvilinear |
---|
3019 | else if(lat_rank.eq.2.and.lon_rank.eq.2.and.\ |
---|
3020 | all(lat_dims.eq.lon_dims)) then |
---|
3021 | grid_type = "curvilinear" |
---|
3022 | ; |
---|
3023 | ; Test for unstructured (what a mess). The extra long test is to |
---|
3024 | ; make sure this is not possibly a rectilinear grid. |
---|
3025 | ; |
---|
3026 | else if(lat_rank.eq.1.and.lon_rank.eq.1.and.lat_dims.eq.lon_dims.and.\ |
---|
3027 | ((var_rank.eq.1.and.var_dims.eq.lat_dims).or.\ |
---|
3028 | (var_rank.gt.1.and.var_dims(var_rank-1).eq.lat_dims.and.\ |
---|
3029 | var_dims(var_rank-2).ne.var_dims(var_rank-1)))) then |
---|
3030 | grid_type = "unstructured" |
---|
3031 | end if |
---|
3032 | end if |
---|
3033 | end if |
---|
3034 | end if |
---|
3035 | end if |
---|
3036 | |
---|
3037 | ;---If grid_type is still "unknown", then we can't continue. |
---|
3038 | if(grid_type.eq."unknown") then |
---|
3039 | print("get_src_grid_info: " + grid_type_name + " and/or " + \ |
---|
3040 | grid_lat_name + "/" + grid_lon_name + " were not set.") |
---|
3041 | print(" Cannot determine the " + grid_long_string + \ |
---|
3042 | " grid type.") |
---|
3043 | exit |
---|
3044 | end if |
---|
3045 | |
---|
3046 | if(DEBUG) then |
---|
3047 | print("get_src_grid_info: " + grid_long_string + \ |
---|
3048 | " grid type is '" + grid_type + "'") |
---|
3049 | end if |
---|
3050 | |
---|
3051 | if(grid_type.eq."rectilinear") then |
---|
3052 | latlon_dims = (/lat_dims,lon_dims/) |
---|
3053 | left_rank = var_rank-2 |
---|
3054 | else if(grid_type.eq."curvilinear") then |
---|
3055 | latlon_dims = lat_dims |
---|
3056 | left_rank = var_rank-2 |
---|
3057 | else if(grid_type.eq."unstructured") then |
---|
3058 | latlon_dims = lat_dims |
---|
3059 | left_rank = var_rank-1 |
---|
3060 | else |
---|
3061 | return([/grid_type/]) |
---|
3062 | end if |
---|
3063 | end if |
---|
3064 | end if |
---|
3065 | |
---|
3066 | if(left_rank.ge.1) |
---|
3067 | left_dims = var_dims(0:left_rank-1) |
---|
3068 | else |
---|
3069 | left_dims = -1 |
---|
3070 | end if |
---|
3071 | |
---|
3072 | return([/grid_type,latlon_dims,lat_vals,lon_vals,left_dims/]) |
---|
3073 | end |
---|
3074 | |
---|
3075 | ;---------------------------------------------------------------------- |
---|
3076 | ; Note: this routine should be combined with "get_src_grid_info" to |
---|
3077 | ; make just one "get_grid_info" routine. For now, any changes made to |
---|
3078 | ; this routine should potentially be done to "get_src_grid_info". |
---|
3079 | ; |
---|
3080 | ; Given an option variable, this function returns information about |
---|
3081 | ; the destination grid and the lat/lon values, if appropriate. |
---|
3082 | ; |
---|
3083 | ; The lat/lon values can be provided via: |
---|
3084 | ; |
---|
3085 | ; - DstGridLat/DstGridLon |
---|
3086 | ; |
---|
3087 | ; - By a grid type of "5deg", "1x1", "2x3", etc. |
---|
3088 | ;---------------------------------------------------------------------- |
---|
3089 | undef("get_dst_grid_info") |
---|
3090 | function get_dst_grid_info(opt) |
---|
3091 | local opt2, grid_type_name, grid_lat_name, grid_lon_name, islatlon, \ |
---|
3092 | grid_string, lat_dims, lon_dims, slat_dims, slon_dims, \ |
---|
3093 | lat_rank, lon_rank, grid_long_string |
---|
3094 | begin |
---|
3095 | opt2 = opt |
---|
3096 | grid_string = "Dst" |
---|
3097 | grid_long_string = "destination" |
---|
3098 | |
---|
3099 | DEBUG = isatt_logical_true(opt2,"Debug") |
---|
3100 | |
---|
3101 | ;---List of some of the possible attributes attached to "opt". |
---|
3102 | grid_type_name = grid_string + "GridType" |
---|
3103 | grid_lat_name = grid_string + "GridLat" |
---|
3104 | grid_lon_name = grid_string + "GridLon" |
---|
3105 | |
---|
3106 | grid_type = str_lower(get_att_value(opt2,grid_type_name,"unknown")) |
---|
3107 | |
---|
3108 | ;---Check if DstGridLat/DstGridLon were specified. |
---|
3109 | if(isatt(opt2,grid_lat_name).and.isatt(opt2,grid_lon_name)) then |
---|
3110 | islatlon = True |
---|
3111 | lat_vals = opt2@$grid_lat_name$ |
---|
3112 | lon_vals = opt2@$grid_lon_name$ |
---|
3113 | ;---Better be a grid like "5deg" or "2x3"! |
---|
3114 | else |
---|
3115 | islatlon = False ; No lat/lon values specified |
---|
3116 | end if |
---|
3117 | |
---|
3118 | if(grid_type.eq."unknown".and..not.islatlon) then |
---|
3119 | print("get_dst_grid_info: can't determine what type of " + \ |
---|
3120 | grid_long_string + " grid you have.") |
---|
3121 | exit |
---|
3122 | end if |
---|
3123 | |
---|
3124 | ;---rectilinear, curvilinear, and unstructured require lat/lon values. |
---|
3125 | if(any(grid_type.eq.(/"rectilinear","curvilinear","unstructured"/)).and.\ |
---|
3126 | .not.islatlon) then |
---|
3127 | print("get_dst_grid_info: no lat/lon values provided for type of grid specified (" + grid_type + ").") |
---|
3128 | exit |
---|
3129 | end if |
---|
3130 | |
---|
3131 | if(grid_type.eq."unknown".and..not.islatlon) then |
---|
3132 | print("get_dst_grid_info: can't determine type of grid you have.") |
---|
3133 | exit |
---|
3134 | end if |
---|
3135 | if(islatlon) then |
---|
3136 | lat_dims = dimsizes(lat_vals) |
---|
3137 | lon_dims = dimsizes(lon_vals) |
---|
3138 | lat_rank = dimsizes(lat_dims) |
---|
3139 | lon_rank = dimsizes(lon_dims) |
---|
3140 | slat_dims = tostring(lat_dims) |
---|
3141 | slon_dims = tostring(lon_dims) |
---|
3142 | |
---|
3143 | if(DEBUG) then |
---|
3144 | print("get_dst_grid_info: " + grid_long_string + \ |
---|
3145 | " lat dims = (" + str_join(slat_dims,",") + ")") |
---|
3146 | print("get_dst_grid_info: " + grid_long_string + \ |
---|
3147 | " lon dims = (" + str_join(slon_dims,",") + ")") |
---|
3148 | end if |
---|
3149 | |
---|
3150 | if(lat_rank.gt.2.or.lon_rank.gt.2.or.lat_rank.ne.lon_rank) then |
---|
3151 | print("get_dst_grid_info: invalid " + grid_long_string + \ |
---|
3152 | " lat/lon given.") |
---|
3153 | exit |
---|
3154 | end if |
---|
3155 | |
---|
3156 | ; |
---|
3157 | ; If grid_type still unknown at this point, try to guess at it. |
---|
3158 | ; |
---|
3159 | ; Note: if lat_dims and lon_dims are both 1D and equal in length, |
---|
3160 | ; this can be either an unstructured or rectilinear grid. In |
---|
3161 | ; this case, the user *must* set "grid_type" to either "rectilinear" |
---|
3162 | ; or "unstructured" so that it's clear which one this is. |
---|
3163 | ; |
---|
3164 | if(grid_type.eq."unknown") then |
---|
3165 | if(lat_rank.eq.1.and.lon_rank.eq.1.and.lat_dims.ne.lon_dims) then |
---|
3166 | grid_type = "rectilinear" |
---|
3167 | else if(lat_rank.eq.2.and.lon_rank.eq.2.and.\ |
---|
3168 | all(lat_dims.eq.lon_dims)) then |
---|
3169 | grid_type = "curvilinear" |
---|
3170 | end if |
---|
3171 | end if |
---|
3172 | end if |
---|
3173 | end if |
---|
3174 | |
---|
3175 | ;---If grid_type is still "unknown", then we can't continue. |
---|
3176 | if(grid_type.eq."unknown") then |
---|
3177 | if(lat_rank.eq.1.and.lon_rank.eq.1.and.lat_dims.eq.lon_dims) then |
---|
3178 | print("get_dst_grid_info: Error: the lat/lon arrays are both 1D and the same length.") |
---|
3179 | print(" This could be a rectilinear or unstructured grid. Please set 'grid_type'") |
---|
3180 | print(" to 'rectilinear' or 'unstructured'.") |
---|
3181 | else |
---|
3182 | print("get_dst_grid_info: " + grid_type_name + " and/or " + \ |
---|
3183 | grid_lat_name + "/" + grid_lon_name + " were not set.") |
---|
3184 | print(" Cannot determine the " + grid_long_string + \ |
---|
3185 | " grid type.") |
---|
3186 | end if |
---|
3187 | exit |
---|
3188 | end if |
---|
3189 | |
---|
3190 | if(grid_type.eq."rectilinear") then |
---|
3191 | latlon_dims = (/lat_dims,lon_dims/) |
---|
3192 | else if(grid_type.eq."curvilinear".or.grid_type.eq."unstructured") then |
---|
3193 | latlon_dims = lat_dims |
---|
3194 | end if |
---|
3195 | end if |
---|
3196 | |
---|
3197 | if(islatlon) then |
---|
3198 | return([/grid_type,latlon_dims,lat_vals,lon_vals/]) |
---|
3199 | else |
---|
3200 | return([/grid_type/]) |
---|
3201 | end if |
---|
3202 | end |
---|
3203 | |
---|
3204 | ;====================================================================== |
---|
3205 | ; This procedure is a generic one that writes the description of a |
---|
3206 | ; source or destination grid to a NetCDF file. It distinguishes |
---|
3207 | ; between the source and data grids via the "which_grid" option, |
---|
3208 | ; and also via the "opt" attributes which can start with "Src" or |
---|
3209 | ; "Dst". |
---|
3210 | ; |
---|
3211 | ; Arguments: |
---|
3212 | ; srcData - variable to be regridded |
---|
3213 | ; which_grid - must be "Src" or "Dst" (or "source", "destination") |
---|
3214 | ; opt - logical variable with attributes |
---|
3215 | ;====================================================================== |
---|
3216 | undef("write_grid_description_file") |
---|
3217 | procedure write_grid_description_file(srcData,which_grid,opt) |
---|
3218 | local opt_new, opt2, grid_type, lat_dims, lon_dims, \ |
---|
3219 | lat_rank, lon_rank, grid_file_name, grid_lat_name, DEBUG, \ |
---|
3220 | grid_string, grid_lon_name, grid_type_name, spc_atts, \ |
---|
3221 | lat_vals, lon_vals, gen_atts, tmp_att, i, grid_type |
---|
3222 | begin |
---|
3223 | opt_new = True ; For constructing new options list |
---|
3224 | opt2 = opt ; Make a copy so we can modify it |
---|
3225 | |
---|
3226 | DEBUG = isatt_logical_true(opt2,"Debug") |
---|
3227 | |
---|
3228 | ;---Test which_grid |
---|
3229 | valid_src_names = (/"src","source"/) |
---|
3230 | valid_dst_names = (/"dst","destination"/) |
---|
3231 | if(any(str_lower(which_grid).eq.valid_src_names)) then |
---|
3232 | grid_string = "Src" |
---|
3233 | grid_long_string = "source" |
---|
3234 | else if(any(str_lower(which_grid).eq.valid_dst_names)) then |
---|
3235 | grid_string = "Dst" |
---|
3236 | grid_long_string = "destination" |
---|
3237 | else |
---|
3238 | grid_string = "" |
---|
3239 | end if |
---|
3240 | end if |
---|
3241 | if(grid_string.eq."") then |
---|
3242 | print("write_grid_description_file: don't recognize value for second " + \ |
---|
3243 | "argument: '" + which_grid + "'") |
---|
3244 | exit |
---|
3245 | end if |
---|
3246 | |
---|
3247 | ; |
---|
3248 | ; Some attribute options cannot both be set if they have |
---|
3249 | ; different values. Check for them here. |
---|
3250 | ; |
---|
3251 | check_both_atts(opt2,grid_string+"Overwrite","Overwrite",False) |
---|
3252 | check_both_atts(opt2,grid_string+"ForceOverwrite","ForceOverwrite",False) |
---|
3253 | |
---|
3254 | ;---List of some of the possible attributes attached to "opt". |
---|
3255 | grid_file_name = grid_string + "FileName" |
---|
3256 | grid_type_name = grid_string + "GridType" |
---|
3257 | |
---|
3258 | ; |
---|
3259 | ; Check for generic attributes to pass through; that is, |
---|
3260 | ; attributes like "Debug". |
---|
3261 | ; |
---|
3262 | gen_atts = (/"GridCornerDelta","PrintTimings","Overwrite","ForceOverwrite","Debug"/) |
---|
3263 | do i=0,dimsizes(gen_atts)-1 |
---|
3264 | if(isatt(opt2,gen_atts(i))) then |
---|
3265 | opt_new@$gen_atts(i)$ = opt2@$gen_atts(i)$ |
---|
3266 | end if |
---|
3267 | end do |
---|
3268 | ; |
---|
3269 | ; Check for specific attributes to pass through; that is, |
---|
3270 | ; attributes that start with "Src" or "Dst". |
---|
3271 | ; |
---|
3272 | ; If the generic form of the attribute has already been |
---|
3273 | ; set, like "opt@Overwrite", then ignore the specific |
---|
3274 | ; attribute, like "opt@SrcOverwrite". |
---|
3275 | ; |
---|
3276 | spc_atts = (/"Mask2D","TriangularMesh","LargeFile",\ |
---|
3277 | "Title","LLCorner","URCorner", \ |
---|
3278 | "Rotation","GridCornerLat","GridCornerLon",\ |
---|
3279 | "InputFileName","Overwrite","ForceOverwrite"/) |
---|
3280 | do i=0,dimsizes(spc_atts)-1 |
---|
3281 | tmp_att = grid_string + spc_atts(i) |
---|
3282 | if(isatt(opt2,tmp_att)) then |
---|
3283 | opt_new@$spc_atts(i)$ = opt2@$tmp_att$ |
---|
3284 | end if |
---|
3285 | end do |
---|
3286 | |
---|
3287 | ; |
---|
3288 | ; Error checking added in V6.1.1 to make sure user doesn't |
---|
3289 | ; accidentally specify SrcFileName or DstFileName to be |
---|
3290 | ; the original input file names. |
---|
3291 | ; |
---|
3292 | if(isatt(opt2,grid_file_name).and.isfilepresent(opt2@$grid_file_name$).and.\ |
---|
3293 | .not.is_SCRIP(opt2@$grid_file_name$,False).and.\ |
---|
3294 | .not.is_ESMF(opt2@$grid_file_name$,False)) then |
---|
3295 | print("ESMF_regrid: error: the file name given for the output " + grid_long_string) |
---|
3296 | print(" grid file (via the special option '" + grid_string + "FileName') already exists") |
---|
3297 | print(" ('" + opt2@$grid_file_name$ + "'), but doesn't appear to be a SCRIP") |
---|
3298 | print(" file. Make sure you are not overwriting any original data files!") |
---|
3299 | print("") |
---|
3300 | print(" Remember: '" + grid_string + "FileName' should be set to the name of the") |
---|
3301 | print(" *output* " + grid_long_string + " grid description file that will be") |
---|
3302 | print(" created, and not the name of the original input file that") |
---|
3303 | print(" contains your data or destination grid. Use '" + grid_string + "InputFileName'") |
---|
3304 | print(" for that purpose.") |
---|
3305 | exit |
---|
3306 | end if |
---|
3307 | |
---|
3308 | if(grid_string.eq."Src") then |
---|
3309 | grid_info_list = get_src_grid_info(opt2,srcData) |
---|
3310 | else |
---|
3311 | grid_info_list = get_dst_grid_info(opt2) |
---|
3312 | end if |
---|
3313 | nlist = ListCount(grid_info_list) |
---|
3314 | grid_type = grid_info_list[0] |
---|
3315 | if(nlist.gt.1) then |
---|
3316 | latlon_dims = grid_info_list[1] |
---|
3317 | lat_vals = grid_info_list[2] |
---|
3318 | lon_vals = grid_info_list[3] |
---|
3319 | end if |
---|
3320 | |
---|
3321 | ;---Check if user set a filename for the description file |
---|
3322 | grid_file = get_att_value(opt2,grid_file_name,grid_long_string + \ |
---|
3323 | "_grid_file.nc") |
---|
3324 | if(grid_type.eq."rectilinear") then |
---|
3325 | rectilinear_to_SCRIP(grid_file,lat_vals,lon_vals,opt_new) |
---|
3326 | else if(grid_type.eq."curvilinear") then |
---|
3327 | curvilinear_to_SCRIP(grid_file,lat_vals,lon_vals,opt_new) |
---|
3328 | else if(grid_type.eq."unstructured") then |
---|
3329 | unstructured_to_ESMF(grid_file,lat_vals,lon_vals,opt_new) |
---|
3330 | |
---|
3331 | ;---Make sure SrcESMF or DstESMF is set (the default is to assume SCRIP) |
---|
3332 | tmp_att = grid_string + "ESMF" |
---|
3333 | opt@$tmp_att$ = True |
---|
3334 | else |
---|
3335 | latlon_to_SCRIP(grid_file,grid_type,opt_new) |
---|
3336 | opt@$grid_type_name$ = "rectilinear" |
---|
3337 | end if |
---|
3338 | end if |
---|
3339 | end if |
---|
3340 | |
---|
3341 | ;---Save the name of the description file and grid type |
---|
3342 | if(.not.isatt(opt,grid_file_name)) then |
---|
3343 | opt@$grid_file_name$ = grid_file |
---|
3344 | end if |
---|
3345 | if(.not.isatt(opt,grid_type_name)) then |
---|
3346 | opt@$grid_type_name$ = grid_type |
---|
3347 | end if |
---|
3348 | end |
---|
3349 | |
---|
3350 | ;====================================================================== |
---|
3351 | ; This function combines all the regridding steps: |
---|
3352 | ; 1) Describing the source and destination grids |
---|
3353 | ; 2) Generating the weights |
---|
3354 | ; 3) Apply the weights to a given variable |
---|
3355 | ; |
---|
3356 | ; This function recognizes the following options |
---|
3357 | ; |
---|
3358 | ; Recognized attributes for "Opt": |
---|
3359 | ; SrcLLCorner / SrcURCorner - defaults to (-90,-180) / ( 90, 180) |
---|
3360 | ; DstLLCorner / DstURCorner - defaults to (-90,-180) / ( 90, 180) |
---|
3361 | ; SrcRotation / DstRotation - rotation angle |
---|
3362 | ; |
---|
3363 | ; SrcGridCornerLat,SrcGridCornerLon / DstGridCornerLat,DstGridCornerLon |
---|
3364 | ; - if set, then these values are used rather than calculating the |
---|
3365 | ; corner points for the cells |
---|
3366 | ; |
---|
3367 | ; SrcFileName / DstFileName |
---|
3368 | ; SrcGridType / SrcGridLat / SrcGridLon |
---|
3369 | ; DstGridType / DstGridLat / DstGridLon |
---|
3370 | ; SrcMask2D / DstMask2D |
---|
3371 | ; SrcLargeFile / DstLargeFile |
---|
3372 | ; WgtFileName |
---|
3373 | ; SkipSrcGrid / SkipDstGrid / SkipWgtGen |
---|
3374 | ; Debug |
---|
3375 | ; PrintTimings |
---|
3376 | ; ForceOverwrite |
---|
3377 | ; Overwrite |
---|
3378 | ;====================================================================== |
---|
3379 | undef("ESMF_regrid") |
---|
3380 | function ESMF_regrid(srcData:numeric, opt[1]:logical) |
---|
3381 | local opt2, src_dims, src_rank, src_grid_type, src_grid_name, \ |
---|
3382 | src_lat_dims, src_lon_dims, src_lat_rank, src_lon_rank, \ |
---|
3383 | dst_dims, dst_rank, dst_grid_type, dst_grid_name, \ |
---|
3384 | dst_lat_dims, dst_lon_dims, dst_lat_rank, dst_lon_rank, \ |
---|
3385 | dstlat, dstlon, wgt_file_name, remove_dst_file, \ |
---|
3386 | remove_src_file, remove_wgt_file, DEBUG |
---|
3387 | begin |
---|
3388 | ;---Check for certain options that must be set. |
---|
3389 | if(.not.opt) then |
---|
3390 | print("ESMF_regrid: opt was set to False. Can't continue.") |
---|
3391 | exit |
---|
3392 | end if |
---|
3393 | |
---|
3394 | DEBUG = isatt_logical_true(opt,"Debug") |
---|
3395 | |
---|
3396 | ;---Make a copy so we don't modify original opt. |
---|
3397 | opt2 = opt |
---|
3398 | |
---|
3399 | ;---Check for incompatible attributes set by user |
---|
3400 | check_both_atts(opt2,"RemoveFiles","RemoveSrcFile",False) |
---|
3401 | check_both_atts(opt2,"RemoveFiles","RemoveDstFile",False) |
---|
3402 | check_both_atts(opt2,"RemoveFiles","RemoveWgtFile",False) |
---|
3403 | |
---|
3404 | ; |
---|
3405 | ; Check if any of the source, grid, or weight files are |
---|
3406 | ; to be removed after this function is done. It's easier |
---|
3407 | ; to let this routine handle it, than ESMF_regrid_gen_weights, |
---|
3408 | ; so just do it. |
---|
3409 | ; |
---|
3410 | remove_src_file = False |
---|
3411 | remove_dst_file = False |
---|
3412 | remove_wgt_file = False |
---|
3413 | if(isatt_logical_true(opt2,"RemoveFiles")) then |
---|
3414 | delete(opt2@RemoveFiles) |
---|
3415 | remove_src_file = True |
---|
3416 | remove_dst_file = True |
---|
3417 | remove_wgt_file = True |
---|
3418 | end if |
---|
3419 | if(isatt_logical_true(opt2,"RemoveSrcFile")) then |
---|
3420 | delete(opt2@RemoveSrcFile) |
---|
3421 | remove_src_file = True |
---|
3422 | end if |
---|
3423 | if(isatt_logical_true(opt2,"RemoveDstFile")) then |
---|
3424 | delete(opt2@RemoveDstFile) |
---|
3425 | remove_dst_file = True |
---|
3426 | end if |
---|
3427 | if(isatt_logical_true(opt2,"RemoveWgtFile")) then |
---|
3428 | delete(opt2@RemoveWgtFile) |
---|
3429 | remove_wgt_file = True |
---|
3430 | end if |
---|
3431 | |
---|
3432 | ;---Write source grid description file |
---|
3433 | if(.not.isatt_logical_true(opt2,"SkipSrcGrid")) then |
---|
3434 | write_grid_description_file(srcData,"Src",opt2) |
---|
3435 | else |
---|
3436 | if(DEBUG) then |
---|
3437 | print("ESMF_regrid: skipping generation of source grid.") |
---|
3438 | end if |
---|
3439 | end if |
---|
3440 | |
---|
3441 | ;---Write destination grid description file |
---|
3442 | if(.not.isatt_logical_true(opt2,"SkipDstGrid")) then |
---|
3443 | write_grid_description_file(srcData,"Dst",opt2) |
---|
3444 | else |
---|
3445 | if(DEBUG) then |
---|
3446 | print("ESMF_regrid: skipping generation of destination grid.") |
---|
3447 | print(" You may need to set DstGridType.") |
---|
3448 | end if |
---|
3449 | end if |
---|
3450 | |
---|
3451 | ;-------------------------------------------------- |
---|
3452 | ; Section to generate the weights |
---|
3453 | ;-------------------------------------------------- |
---|
3454 | wgt_file_name = get_att_value(opt2,"WgtFileName","weights_file.nc") |
---|
3455 | if(.not.isatt_logical_true(opt2,"SkipWgtGen")) then |
---|
3456 | ; |
---|
3457 | ; If skipped the source and/or destination grid generation phase, then |
---|
3458 | ; the user must supply the names of the grid files explicitly. |
---|
3459 | ; |
---|
3460 | if(isatt_logical_true(opt,"SkipSrcGrid").and.isatt(opt,"SrcFileName")) then |
---|
3461 | src_grid_file = opt@SrcFileName |
---|
3462 | else if(.not.isatt_logical_true(opt,"SkipSrcGrid").and.isatt(opt2,"SrcFileName")) then |
---|
3463 | src_grid_file = opt2@SrcFileName |
---|
3464 | else |
---|
3465 | print("ESMF_regrid: need the name of the source grid file (SrcFileName)") |
---|
3466 | exit |
---|
3467 | end if |
---|
3468 | end if |
---|
3469 | if(isatt_logical_true(opt,"SkipDstGrid").and.isatt(opt,"DstFileName")) then |
---|
3470 | dst_grid_file = opt@DstFileName |
---|
3471 | else if(.not.isatt_logical_true(opt,"SkipDstGrid").and.isatt(opt2,"DstFileName")) then |
---|
3472 | dst_grid_file = opt2@DstFileName |
---|
3473 | else |
---|
3474 | print("ESMF_regrid: need the name of the destination grid file (DstFileName)") |
---|
3475 | exit |
---|
3476 | end if |
---|
3477 | end if |
---|
3478 | ESMF_regrid_gen_weights(src_grid_file, dst_grid_file, wgt_file_name, opt2) |
---|
3479 | else |
---|
3480 | if(DEBUG) then |
---|
3481 | print("ESMF_regrid: skipping generation of weights file.") |
---|
3482 | end if |
---|
3483 | end if |
---|
3484 | |
---|
3485 | ; |
---|
3486 | ; If patch method used, "sneak" this knowledge into the weights gen |
---|
3487 | ; routine so that we can get the correct "map_method" for the |
---|
3488 | ; "remap" attribute. |
---|
3489 | ; |
---|
3490 | if(opt2.and.isatt(opt2,"InterpMethod").and. \ |
---|
3491 | str_lower(opt2@InterpMethod).eq."patch") then |
---|
3492 | opt2@FixMapMethodForPatch = True |
---|
3493 | end if |
---|
3494 | |
---|
3495 | ;---Regrid the input variable |
---|
3496 | srcData_regrid = ESMF_regrid_with_weights(srcData,wgt_file_name,opt2) |
---|
3497 | |
---|
3498 | ;---Copy variable attributes and coordinate arrays if requested. |
---|
3499 | ESMF_copy_varmeta(srcData,srcData_regrid,wgt_file_name,opt2) |
---|
3500 | |
---|
3501 | ;---Clean up, if desired |
---|
3502 | if (remove_src_file) then |
---|
3503 | if(DEBUG) then |
---|
3504 | print("ESMF_regrid: By request, removing '" + src_grid_file + "'") |
---|
3505 | end if |
---|
3506 | system("rm -rf " + src_grid_file) |
---|
3507 | end if |
---|
3508 | |
---|
3509 | if (remove_dst_file) then |
---|
3510 | if(DEBUG) then |
---|
3511 | print("ESMF_regrid: By request, removing '" + dst_grid_file + "'") |
---|
3512 | end if |
---|
3513 | system("rm -rf " + dst_grid_file) |
---|
3514 | end if |
---|
3515 | |
---|
3516 | if (remove_wgt_file) then |
---|
3517 | if(DEBUG) then |
---|
3518 | print("ESMF_regrid: By request, removing '" + wgt_file_name + "'") |
---|
3519 | end if |
---|
3520 | system("rm -rf " + wgt_file_name) |
---|
3521 | end if |
---|
3522 | |
---|
3523 | return(srcData_regrid) |
---|
3524 | end |
---|
3525 | |
---|
3526 | ;====================================================================== |
---|
3527 | ; This routine is obsolete in V6.1.0 (not in V6.1.0-beta). It's |
---|
3528 | ; replaced by ESMF_copy_varcoords. I'm not sure it was |
---|
3529 | ; ever used... |
---|
3530 | ;====================================================================== |
---|
3531 | undef("retrieve_dstGrid_lat") |
---|
3532 | function retrieve_dstGrid_lat(fileName[1]:string) |
---|
3533 | local fid, dst_grid_dims |
---|
3534 | begin |
---|
3535 | if (.not.isfilepresent(fileName)) then |
---|
3536 | print("retrieve_dstGrid_lat: cannot open '" + fileName + "'.") |
---|
3537 | exit |
---|
3538 | end if |
---|
3539 | |
---|
3540 | fid = addfile(fileName,"r") |
---|
3541 | dst_grid_dims = fid->dst_grid_dims |
---|
3542 | lat = reshape( fid->yc_b , dst_grid_dims(::-1)) |
---|
3543 | return(lat) |
---|
3544 | end ; of retrieve_dstGrid_lat |
---|
3545 | |
---|
3546 | ;====================================================================== |
---|
3547 | ; This routine is obsolete in V6.1.0 (not in V6.1.0-beta). It's |
---|
3548 | ; replaced by ESMF_copy_varcoords. I'm not sure it was |
---|
3549 | ; ever used... |
---|
3550 | ;====================================================================== |
---|
3551 | undef("retrieve_dstGrid_lon") |
---|
3552 | function retrieve_dstGrid_lon(fileName[1]:string) |
---|
3553 | local fid, dst_grid_dims |
---|
3554 | begin |
---|
3555 | if (.not.isfilepresent(fileName)) then |
---|
3556 | print("retrieve_dstGrid_lon: cannot open '" + fileName + "'.") |
---|
3557 | exit |
---|
3558 | end if |
---|
3559 | |
---|
3560 | fid = addfile(fileName,"r") |
---|
3561 | dst_grid_dims = fid->dst_grid_dims |
---|
3562 | lon = reshape( fid->xc_b , dst_grid_dims(::-1)) |
---|
3563 | return(lon) |
---|
3564 | |
---|
3565 | end ; of retrieve_dstGrid_lon |
---|
3566 | |
---|
3567 | ;====================================================================== |
---|
3568 | ; This routine is obsolete in V6.1.0 (not in V6.1.0-beta). It's |
---|
3569 | ; replaced by ESMF_copy_varcoords. I'm not sure it was |
---|
3570 | ; ever used... |
---|
3571 | ;====================================================================== |
---|
3572 | undef("retrieve_srcGrid_lat") |
---|
3573 | function retrieve_srcGrid_lat(fileName[1]:string) |
---|
3574 | local fid, src_grid_dims |
---|
3575 | begin |
---|
3576 | if (.not.isfilepresent(fileName)) then |
---|
3577 | print("retrieve_srcGrid_lat: cannot open '" + fileName + "'.") |
---|
3578 | exit |
---|
3579 | end if |
---|
3580 | |
---|
3581 | fid = addfile(fileName,"r") |
---|
3582 | src_grid_dims = fid->src_grid_dims |
---|
3583 | lat = reshape( fid->yc_a , src_grid_dims(::-1)) |
---|
3584 | return(lat) |
---|
3585 | end ; of retrieve_srcGrid_lat |
---|
3586 | |
---|
3587 | ;====================================================================== |
---|
3588 | ; This routine is obsolete in V6.1.0 (not in V6.1.0-beta). It's |
---|
3589 | ; replaced by ESMF_copy_varcoords. I'm not sure it was |
---|
3590 | ; ever used... |
---|
3591 | ;====================================================================== |
---|
3592 | undef("retrieve_srcGrid_lon") |
---|
3593 | function retrieve_srcGrid_lon(fileName[1]:string) |
---|
3594 | local fid, src_grid_dims |
---|
3595 | begin |
---|
3596 | if (.not.isfilepresent(fileName)) then |
---|
3597 | print("retrieve_srcGrid_lon: cannot open '" + fileName + "'.") |
---|
3598 | exit |
---|
3599 | end if |
---|
3600 | |
---|
3601 | fid = addfile(fileName,"r") |
---|
3602 | src_grid_dims = fid->src_grid_dims |
---|
3603 | lon = reshape( fid->xc_a , src_grid_dims(::-1)) |
---|
3604 | return(lon) |
---|
3605 | end ; of retrieve_srcGrid_lon |
---|