1515import cftime
1616import glob
1717
18+ """
19+ << TO DO >>:
20+ 1) Fix xtime after in final output files
21+ 2) Mask out Jakobshaven/Ilulissat for icebergFjordMask
22+ """
1823class eccoToMaliInterp :
1924
2025 def __init__ (self ):
@@ -30,6 +35,9 @@ def __init__(self):
3035 parser .add_argument ("--method" , dest = "method" , type = str , help = "Remapping method, either 'bilinear' or 'neareststod'" , default = 'conserve' )
3136 parser .add_argument ("--outFile" , dest = "outputFile" , help = "Desired name of output file" , metavar = "FILENAME" , default = "ecco_to_mali_remapped.nc" )
3237 parser .add_argument ("--meshVars" , dest = "includeMeshVars" , help = "whether to include mesh variables in resulting MALI forcing file" , action = 'store_true' )
38+ parser .add_argument ("--iceAreaThresh" , dest = "iceAreaThresh" , type = float , help = "Threshold sea ice fraction used to define icebergFjordMask." , default = 0.5 )
39+ parser .add_argument ("--extrapIters" , dest = "extrapIters" , type = str , help = "Number of sea ice extrapolation iterations when regridding" , default = '50' )
40+ parser .add_argument ("--keepTmpFiles" , dest = "keepTmpFiles" , help = "Whether to keep temporary netcdf files created throughout script. Useful for debugging" , action = "store_true" )
3341 args = parser .parse_args ()
3442 self .options = args
3543
@@ -239,15 +247,17 @@ def merge_MITgcm_files_to_unstruct_grid(self):
239247 DA_sia = xr .DataArray (sia .astype ('float64' ), dims = ("Time" , "nCells" ))
240248 DA_sia .attrs ['long_name' ] = 'Sea ice fractional ice-covered area [0 to 1]'
241249 DA_sia .attrs ['units' ] = 'm2/m2'
242- ds_unstruct ['iceAreaCell' ] = DA_sia
250+ # create separate netcdf file for SIA so it can be extrapolated during remapping
251+ ds_sia = xr .Dataset ()
252+ ds_sia ['iceAreaCell' ] = DA_sia
253+ self .siaFile = 'sia.tmp.nc'
254+ ds_sia .to_netcdf (self .siaFile )
255+ ds_sia .close ()
243256
244257 if 'orig3dOceanMask' in locals ():
245258 orig3dOceanMask = orig3dOceanMask [0 :len (z ),:]
246259 orig3dOceanMask = np .tile (orig3dOceanMask [np .newaxis , :, :], (nt , 1 , 1 ))
247260 DA_o3dm = xr .DataArray (orig3dOceanMask .astype ('float64' ), dims = ("Time" , "nISMIP6OceanLayers" , "nCells" ))
248- DA_o3dm .attrs ['long_name' ] = ("3D mask of original valid ocean data. Because it is 3d,"
249- " it can include the ocean domain inside ice-shelf cavities."
250- " orig3dOceanMask is altered online to include ice-dammed inland seas" )
251261 ds_unstruct ['orig3dOceanMask' ] = DA_o3dm
252262
253263 self .ecco_unstruct = "ecco_combined_unstructured.tmp.nc"
@@ -261,7 +271,7 @@ def remap_ecco_to_mali(self):
261271 scrip_from_mpas (self .options .maliMesh , mali_scripfile )
262272
263273 #create mapping file
264- print ("Creating Mapping File" )
274+ print ("Creating Primary Mapping File" )
265275 args = ['srun' ,
266276 '-n' , self .options .ntasks , 'ESMF_RegridWeightGen' ,
267277 '--source' , self .ecco_scripfile ,
@@ -292,7 +302,64 @@ def remap_ecco_to_mali(self):
292302 nd = time .time ()
293303 tm = nd - st
294304 print (f"ncremap completed: { tm } seconds" )
295- print ("Remapping completed" )
305+ print ("Primary Remapping completed" )
306+
307+ # repeat remapping for sia, including extrapolation of sia to fill domain
308+ ds_siaScrip = xr .open_dataset (self .ecco_scripfile )
309+ ds_unstruct = xr .open_dataset (self .ecco_unstruct )
310+ o3dm = ds_unstruct ['orig3dOceanMask' ][1 ,1 ,:].values
311+ valid = (o3dm > 0.9999 ).astype ('int32' ) # Account for rounding error
312+ ds_siaScrip ['grid_imask' ] = xr .DataArray (valid .astype ('int32' ),
313+ dims = ('grid_size' ),
314+ attrs = {'units' :'unitless' })
315+ sia_scripfile = 'ecco_sia.scrip.nc'
316+ ds_siaScrip .to_netcdf (sia_scripfile )
317+ ds_siaScrip .close ()
318+
319+ print ("Creating SIA mapping File" )
320+ sia_mapping_file = 'ecco_to_mali_mapping_bilinear_sia.nc'
321+ args = ['srun' ,
322+ '-n' , self .options .ntasks , 'ESMF_RegridWeightGen' ,
323+ '--source' , sia_scripfile ,
324+ '--destination' , mali_scripfile ,
325+ '--weight' , sia_mapping_file ,
326+ '--method' , 'bilinear' ,
327+ '--extrap_method' , 'creep' ,
328+ '--extrap_num_levels' , self .options .extrapIters ,
329+ '--netcdf4' ,
330+ "--dst_regional" , "--src_regional" , '--ignore_unmapped' ]
331+ check_call (args )
332+
333+ #remap
334+ print ("Start SIA remapping ... " )
335+
336+ subprocess .run (["ncatted" , "-a" , "_FillValue,,d,," , self .siaFile ])
337+
338+ print ("ncremap of SIA..." )
339+ st = time .time ()
340+ # remap the input data
341+ siaRemappedOutFile = self .options .outputFile [0 :- 3 ] + '.tmp.sia.remapped.nc'
342+ args_remap = ["ncremap" ,
343+ "-i" , self .siaFile ,
344+ "-o" , siaRemappedOutFile ,
345+ "-m" , sia_mapping_file ]
346+ check_call (args_remap )
347+
348+ nd = time .time ()
349+ tm = nd - st
350+ print (f"ncremap completed: { tm } seconds" )
351+ print ("SIA Remapping completed" )
352+
353+ print ("Applying final edits to remapped file ..." )
354+ # define icebergFjordMask from SIA
355+ ds_sia = xr .open_dataset (siaRemappedOutFile )
356+ iac = ds_sia ['iceAreaCell' ].values
357+ icebergFjordMask = np .zeros (iac .shape )
358+ # Find cells where sea ice fraction exceeds threshold
359+ mask = iac > self .options .iceAreaThresh
360+ icebergFjordMask [mask ] = 1
361+ ifm = xr .DataArray (icebergFjordMask .astype ('int32' ), dims = ("Time" , "nCells" ))
362+ sia = xr .DataArray (iac .astype ('float32' ), dims = ("Time" , "nCells" ))
296363
297364 # During conservative remapping, some valid ocean cells are averaged with invalid ocean cells. Use
298365 # float version of orig3dOceanMask to identify these cells and remove. Resave orig3dOceanMask as int32
@@ -303,25 +370,23 @@ def remap_ecco_to_mali(self):
303370 sal = ds_out ['oceanSalinity' ]
304371
305372 mask = o3dm > 0.9999 #Account for rounding error. Good cells are not exactly 1
306- ds_out ['orig3dOceanMask' ] = o3dm . where (mask , 0 ) .astype ('int32' )
373+ ds_out ['orig3dOceanMask' ] = xr . DataArray (mask .astype ('int32' ), dims = ( "Time" , "nCells" , "nISMIP6OceanLayers" ) )
307374 ds_out ['oceanTemperature' ] = temp .where (mask , 1e36 ).astype ('float64' )
308375 ds_out ['oceanSalinity' ] = sal .where (mask , 1e36 ).astype ('float64' )
376+ # Add sia and icebergBergMask into main file
377+ ds_out ['iceAreaCell' ] = sia
378+ ds_out ['icebergFjordMask' ] = ifm
309379
310380 # Save final output file if not adding mesh variables
311- if self .options .includeMeshVars :
312- altRemappedOutFile = self .options .outputFile [0 :- 3 ] + '.tmp.altRemapped.nc'
313- else :
314- altRemappedOutFile = self .options .outputFile
315-
316- # <<NOTE>>: Creating a new netcdf file like this changes the format of xtime. Need to add
317- # encoding update to maintain original formatting
318- ds_out .to_netcdf (altRemappedOutFile , 'w' )
319- subprocess .run (["ncatted" , "-a" , "_FillValue,,d,," , altRemappedOutFile ])
320-
321- # Transfer MALI mesh variables if needed
322- if self .options .includeMeshVars :
381+ if not self .options .includeMeshVars :
382+ # <<NOTE>>: Creating a new netcdf file like this changes the format of xtime. Need to add
383+ # encoding update to maintain original formatting
384+ ds_out .to_netcdf (self .options .outputFile , unlimited_dims = ['Time' ])
385+ subprocess .run (["ncatted" , "-a" , "_FillValue,,d,," , altRemappedOutFile ])
386+
387+ else :
388+ # Transfer MALI mesh variables if needed
323389 ds_mali = xr .open_dataset (self .options .maliMesh , decode_times = False , decode_cf = False )
324- ds_out = xr .open_dataset (self .options .outputFile , decode_times = False , decode_cf = False , mode = 'r+' )
325390 ds_out ['angleEdge' ] = ds_mali ['angleEdge' ]
326391 ds_out ['areaCell' ] = ds_mali ['areaCell' ]
327392 ds_out ['areaTriangle' ] = ds_mali ['areaTriangle' ]
@@ -366,9 +431,10 @@ def remap_ecco_to_mali(self):
366431 subprocess .run (["ncatted" , "-a" , "_FillValue,,d,," , self .options .outputFile ])
367432
368433 # Remove temporary output files
369- for filename in os .listdir ("." ):
370- if "tmp" in filename and os .path .isfile (filename ):
371- os .remove (filename )
434+ if not self .options .keepTmpFiles :
435+ for filename in os .listdir ("." ):
436+ if "tmp" in filename and os .path .isfile (filename ):
437+ os .remove (filename )
372438
373439 def create_ECCO_scrip_file (self ):
374440
0 commit comments