source: CPL/oasis3-mct/branches/OASIS3-MCT_2.0_branch/examples/test_rmp_esmf/esmf_regridding_unstructured/ESMF_regridding.ncl @ 4775

Last change on this file since 4775 was 4775, checked in by aclsce, 5 years ago
  • Imported oasis3-mct from Cerfacs svn server (not suppotred anymore).

The version has been extracted from https://oasis3mct.cerfacs.fr/svn/branches/OASIS3-MCT_2.0_branch/oasis3-mct@1818

File size: 128.7 KB
Line 
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;======================================================================
279undef("get_start_time")
280function get_start_time()
281local s
282begin
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
289end
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;======================================================================
296undef("print_elapsed_time")
297procedure print_elapsed_time(stime,title)
298local s
299begin
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
307end
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;======================================================================
317undef("isLatorLon")
318function isLatorLon(fid,VarNames[*]:string)
319local NumNames, OutPut, i
320begin
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)
336end
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;***********************************************************************;
348undef("get_att_value")
349function get_att_value(opt,attname:string,default_val)
350local return_val
351begin
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)
359end
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;======================================================================
366undef("isatt_logical_true")
367function isatt_logical_true(Opt[1]:logical,AttName[1]:string)
368begin
369    if (isatt(Opt,AttName).and.islogical(Opt@$AttName$)) then
370        return(Opt@$AttName$)
371    end if
372    return(False)
373end     ; 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;======================================================================
379undef("isatt_logical_false")
380function isatt_logical_false(Opt[1]:logical,AttName[1]:string)
381begin
382    if (isatt(Opt,AttName).and.islogical(Opt@$AttName$).and. \
383        .not.Opt@$AttName$) then
384        return(True)
385    end if
386    return(False)
387end     ; 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;======================================================================
399undef("check_for_file")
400procedure check_for_file(fname,opt)
401local DEBUG
402begin
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
418end
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;======================================================================
425undef("check_both_atts")
426procedure check_both_atts(opt,att1,att2,attval)
427begin
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
436end
437
438;======================================================================
439; This function coerces the type of a variable to the given type.
440; It will also copy the attributes.
441;======================================================================
442undef("totypeof")
443function totypeof(inVar,outType)
444begin
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)
493end     ; 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;======================================================================
499undef("rotate_latlon")
500procedure rotate_latlon(lat:snumeric,lon:snumeric,Rot[1]:numeric)
501local d2r, tmpPoints, latlon_dims
502begin
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)
519end
520
521;======================================================================
522; This functions calculates the mirror of p1 with respect to po.
523;======================================================================
524undef("mirrorP2P")
525function mirrorP2P(p1,po)
526local dVec
527begin
528    dVec    = p1-po
529    MirrorP = po-dVec
530    return(MirrorP)
531end     ; 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;======================================================================
554undef("check_grid_type")
555function check_grid_type(grid_type[1]:string)
556local str, dlat, dlon, nlon, ret
557begin
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)
624end
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;======================================================================
638undef("calc_SCRIP_corners_boundaries")
639procedure calc_SCRIP_corners_boundaries(lat2d[*][*],lon2d[*][*], \
640                                        grid_corner_lat, grid_corner_lon,\
641                                        opt)
642local DEBUG, latlon_dims, grid_corner_lat2d,  grid_corner_lon2d, \
643      nlat, nlon, nlatp1, nlonp1
644begin
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
762end     ; 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;======================================================================
779undef("calc_SCRIP_corners_noboundaries")
780procedure calc_SCRIP_corners_noboundaries(lat2d[*][*],lon2d[*][*], \
781                             grid_corner_lat,grid_corner_lon,opt)
782local latlon_dims, nlat, nlon, Extlon2d, Extlat2d, ExtGridCenter_lat, \
783      ExtGridCenter_lon, tmp, ii, jj, DEBUG
784begin
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
910end     ; 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;======================================================================
917undef("unstructured_to_ESMF")
918procedure unstructured_to_ESMF(FName[1]:string,inLat,inLon,Opt[1]:logical)
919local DEBUG, lat, lon, NodeMask, ElementVertices, title
920begin
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
1117end     ; 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;======================================================================
1134undef("curvilinear_to_SCRIP")
1135procedure curvilinear_to_SCRIP(FName[1]:string,lat2d[*][*]:numeric,\
1136                               lon2d[*][*]:numeric,Opt[1]:logical)
1137local latlon_dims, nlat, nlon, fid, FileAtt, grid_siz, grid_corners, \
1138grid_rank, FDimNames, FDimSizes, FDimUnlim, DummyAtt1, DummyAtt2, \
1139GridCornerLat, GridCornerLon, grid_corner_lat, grid_corner_lon, mask2d, \
1140lat_type, lon_type, DEBUG
1141begin
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
1321end     ; 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;======================================================================
1328undef("rectilinear_to_SCRIP")
1329procedure rectilinear_to_SCRIP(FName[1]:string,lat[*]:numeric,\
1330                               lon[*]:numeric, Opt[1]:logical)
1331local nlat, nlon, grid_center_lat, grid_center_lon, Opt2, FTitle
1332begin
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
1365end     ; 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;======================================================================
1378undef("auto_to_SCRIP")
1379procedure auto_to_SCRIP(srcFile[1]:string,VarName[1]:string, \
1380                        SCRIPFName[1]:string,FTitle[1]:string,Opt[1]:logical)
1381local 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
1387begin
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)
1596end     ; 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;======================================================================
1621undef("latlon_to_SCRIP")
1622procedure latlon_to_SCRIP(FName[1]:string,GridType[1]:string,Opt[1]:logical)
1623local 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
1626begin
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
1759end     ; of latlon_to_SCRIP(...)
1760
1761;======================================================================
1762; Checks if the required dimensions in the SCRIP file are present
1763;======================================================================
1764undef("check_SCRIP_dims")
1765function check_SCRIP_dims(fid[1]:file,verbose[1]:logical)
1766local i, file_dims, scrip_dim_names
1767begin
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)
1779end     ; of check_SCRIP_dims(...)
1780
1781;======================================================================
1782; Checks if the required variables in SCRIP file are present
1783;======================================================================
1784undef("check_SCRIP_vars")
1785function check_SCRIP_vars(fid[1]:file,verbose[1]:logical)
1786local i, scrip_vars
1787begin
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)
1799end     ; check_SCRIP_vars(...)
1800
1801;======================================================================
1802; Checks if the input filename is following SCRIP standard
1803;======================================================================
1804undef("is_SCRIP")
1805function is_SCRIP(FileName[1]:string,verbose[1]:logical)
1806local fid
1807begin
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
1817end     ; of is_SCRIP(...)
1818
1819;======================================================================
1820; Checks if the required dimensions in the ESMF file are present
1821;======================================================================
1822undef("check_ESMF_dims")
1823function check_ESMF_dims(fid[1]:file,verbose[1]:logical)
1824local i, esmf_dim_names, file_dims
1825begin
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)
1839end     ; of check_ESMF_dims(...)
1840
1841;======================================================================
1842; Checks if the required variables in ESMF file are present
1843;======================================================================
1844undef("check_ESMF_vars")
1845function check_ESMF_vars(fid[1]:file,verbose[1]:logical)
1846local i, esmf_vars
1847begin
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)
1860end     ; check_ESMF_vars(...)
1861
1862;======================================================================
1863; Checks if the input filename is following ESMF standard
1864;======================================================================
1865undef("is_ESMF")
1866function is_ESMF(FileName[1]:string,verbose[1]:logical)
1867local fid
1868begin
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
1878end     ; 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;======================================================================
1887undef("retrieve_SCRIP_lat")
1888function retrieve_SCRIP_lat(fileName[1]:string)
1889local fid, grid_dims, grid_center_lat
1890begin
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)
1902end     ; 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;======================================================================
1911undef("retrieve_SCRIP_lon")
1912function retrieve_SCRIP_lon(fileName[1]:string)
1913local fid, grid_dims, grid_center_lon
1914begin
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)
1926end     ; 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;======================================================================
1935undef("retrieve_ESMF_lat")
1936function retrieve_ESMF_lat(fileName[1]:string)
1937local fid, grid_center_lon
1938begin
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)
1949end     ; 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;======================================================================
1958undef("retrieve_ESMF_lon")
1959function retrieve_ESMF_lon(fileName[1]:string)
1960local fid, grid_center_lon
1961begin
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)
1972end     ; 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;======================================================================
1987undef("ESMF_copy_varcoords")
1988procedure ESMF_copy_varcoords(sd:numeric,sd_regrid:numeric,\
1989                              wgt_file_name[1]:string,opt[1]:logical)
1990local 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
1992begin
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
2144end
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;======================================================================
2157undef("ESMF_copy_varmeta")
2158procedure ESMF_copy_varmeta(sd,sd_regrid,wgt_file_name[1]:string, \
2159                            opt[1]:logical)
2160local var_dims, var_rank, dstlat, dstlon, atts_to_skip, natts
2161begin
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
2189end
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;======================================================================
2236undef("ESMF_regrid_gen_weights")
2237procedure ESMF_regrid_gen_weights(srcGridFile[1]:string, dstGridFile[1]:string, \
2238                           wgtFile[1]:string, opt[1]:logical)
2239local DEBUG, NumProc, Executer, ESMFCMD, ESMF_exec, interp_method, pole, opt
2240begin
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
2471end     ; 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;======================================================================
2482undef("ESMF_regrid_with_weights")
2483function ESMF_regrid_with_weights(srcData:numeric,wgtFile[1]:string, opt[1]:logical)
2484local 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
2489begin
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)
2871end     ; 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;----------------------------------------------------------------------
2893undef("get_src_grid_info")
2894function get_src_grid_info(opt,data)
2895local 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
2898begin
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/])
3073end
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;----------------------------------------------------------------------
3089undef("get_dst_grid_info")
3090function get_dst_grid_info(opt)
3091local 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
3094begin
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
3202end
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;======================================================================
3216undef("write_grid_description_file")
3217procedure write_grid_description_file(srcData,which_grid,opt)
3218local 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
3222begin
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
3348end
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;======================================================================
3379undef("ESMF_regrid")
3380function ESMF_regrid(srcData:numeric, opt[1]:logical)
3381local 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
3387begin
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)
3524end
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;======================================================================
3531undef("retrieve_dstGrid_lat")
3532function retrieve_dstGrid_lat(fileName[1]:string)
3533local fid, dst_grid_dims
3534begin
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)
3544end     ; 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;======================================================================
3551undef("retrieve_dstGrid_lon")
3552function retrieve_dstGrid_lon(fileName[1]:string)
3553local fid, dst_grid_dims
3554begin
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   
3565end     ; 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;======================================================================
3572undef("retrieve_srcGrid_lat")
3573function retrieve_srcGrid_lat(fileName[1]:string)
3574local fid, src_grid_dims
3575begin
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)
3585end     ; 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;======================================================================
3592undef("retrieve_srcGrid_lon")
3593function retrieve_srcGrid_lon(fileName[1]:string)
3594local fid, src_grid_dims
3595begin
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)
3605end     ; of retrieve_srcGrid_lon
Note: See TracBrowser for help on using the repository browser.