Skip to content

Commit e4605a3

Browse files
committed
Define icebergFjordMask
Extrapolates iceCellArea to fill ocean cells, and defines icebergFjordMask wherever iceCellArea is above a threshold value
1 parent 1cb7841 commit e4605a3

File tree

1 file changed

+89
-23
lines changed

1 file changed

+89
-23
lines changed

landice/mesh_tools_li/interpolate_ecco_to_mali.py

Lines changed: 89 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
import cftime
1616
import glob
1717

18+
"""
19+
<< TO DO >>:
20+
1) Fix xtime after in final output files
21+
2) Mask out Jakobshaven/Ilulissat for icebergFjordMask
22+
"""
1823
class 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

Comments
 (0)