Skip to content

Commit 588e1cc

Browse files
authored
Merge pull request #778 from xylar/use-paolo-for-dismf
Switch DISMF to Paolo et al. (2023) dataset
2 parents fb568e9 + 5da9d95 commit 588e1cc

File tree

6 files changed

+235
-31
lines changed

6 files changed

+235
-31
lines changed

compass/ocean/tests/global_ocean/files_for_e3sm/remap_ice_shelf_melt.py

+13-13
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
FilesForE3SMStep,
66
)
77
from compass.ocean.tests.global_ocean.init.remap_ice_shelf_melt import (
8-
remap_adusumilli,
8+
remap_paolo,
99
)
1010

1111

@@ -34,13 +34,13 @@ def __init__(self, test_case, init):
3434
super().__init__(test_case, name='remap_ice_shelf_melt', ntasks=512,
3535
min_tasks=1)
3636
self.init = init
37-
filename = 'prescribed_ismf_adusumilli2020.nc'
37+
filename = 'prescribed_ismf_paolo2023.nc'
3838
if init is None:
3939
self.add_input_file(
40-
filename='Adusumilli_2020_iceshelf_melt_rates_2010-2018_v0.h5',
41-
target='Adusumilli_2020_iceshelf_melt_rates_2010-2018_v0.h5',
40+
filename='Paolo_2023_ANT_G1920V01_IceShelfMelt.nc',
41+
target='Paolo_2023_ANT_G1920V01_IceShelfMelt.nc',
4242
database='initial_condition_database',
43-
url='http://library.ucsd.edu/dc/object/bb0448974g/_3_1.h5')
43+
url='https://its-live-data.s3.amazonaws.com/height_change/Antarctica/Floating/ANT_G1920V01_IceShelfMelt.nc') # noqa: E501
4444
elif 'remap_ice_shelf_melt' in self.init.steps:
4545
melt_path = \
4646
self.init.steps['remap_ice_shelf_melt'].path
@@ -54,7 +54,7 @@ def setup(self):
5454
setup input files based on config options
5555
"""
5656
super().setup()
57-
filename = 'prescribed_ismf_adusumilli2020.nc'
57+
filename = 'prescribed_ismf_paolo2023.nc'
5858
if self.with_ice_shelf_cavities:
5959
self.add_output_file(filename=filename)
6060

@@ -67,7 +67,7 @@ def run(self):
6767
if not self.with_ice_shelf_cavities:
6868
return
6969

70-
prefix = 'prescribed_ismf_adusumilli2020'
70+
prefix = 'prescribed_ismf_paolo2023'
7171
suffix = f'{self.mesh_short_name}.{self.creation_date}'
7272

7373
remapped_filename = f'{prefix}.nc'
@@ -77,7 +77,7 @@ def run(self):
7777
logger = self.logger
7878
config = self.config
7979
ntasks = self.ntasks
80-
in_filename = 'Adusumilli_2020_iceshelf_melt_rates_2010-2018_v0.h5'
80+
in_filename = 'Paolo_2023_ANT_G1920V01_IceShelfMelt.nc'
8181

8282
parallel_executable = config.get('parallel', 'parallel_executable')
8383

@@ -86,11 +86,11 @@ def run(self):
8686
mesh_name = self.mesh_short_name
8787
land_ice_mask_filename = 'initial_state.nc'
8888

89-
remap_adusumilli(in_filename, base_mesh_filename,
90-
culled_mesh_filename, mesh_name,
91-
land_ice_mask_filename, remapped_filename,
92-
logger=logger, mpi_tasks=ntasks,
93-
parallel_executable=parallel_executable)
89+
remap_paolo(in_filename, base_mesh_filename,
90+
culled_mesh_filename, mesh_name,
91+
land_ice_mask_filename, remapped_filename,
92+
logger=logger, mpi_tasks=ntasks,
93+
parallel_executable=parallel_executable)
9494

9595
symlink(
9696
os.path.abspath(remapped_filename),

compass/ocean/tests/global_ocean/global_ocean.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ max_depth = autodetect
101101
# the number of vertical levels, always detected automatically
102102
levels = autodetect
103103

104-
# the date the mesh was created as YYMMDD, typically detected automatically
104+
# the date the mesh was created as YYYYMMDD, typically detected automatically
105105
creation_date = autodetect
106106
# The following options are detected from .gitconfig if not explicitly entered
107107
author = autodetect

compass/ocean/tests/global_ocean/init/remap_ice_shelf_melt.py

+217-14
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def __init__(self, test_case, mesh):
3131
3232
mesh : compass.ocean.tests.global_ocean.mesh.Mesh
3333
The test case that produces the mesh for this run
34-
""" # noqa: E501
34+
"""
3535
super().__init__(test_case, name='remap_ice_shelf_melt', ntasks=512,
3636
min_tasks=1)
3737

@@ -55,12 +55,12 @@ def __init__(self, test_case, mesh):
5555
work_dir_target=f'{culled_mesh_path}/land_ice_mask.nc')
5656

5757
self.add_input_file(
58-
filename='Adusumilli_2020_iceshelf_melt_rates_2010-2018_v0.h5',
59-
target='Adusumilli_2020_iceshelf_melt_rates_2010-2018_v0.h5',
58+
filename='Paolo_2023_ANT_G1920V01_IceShelfMelt.nc',
59+
target='Paolo_2023_ANT_G1920V01_IceShelfMelt.nc',
6060
database='initial_condition_database',
61-
url='http://library.ucsd.edu/dc/object/bb0448974g/_3_1.h5')
61+
url='https://its-live-data.s3.amazonaws.com/height_change/Antarctica/Floating/ANT_G1920V01_IceShelfMelt.nc') # noqa: E501
6262

63-
self.add_output_file(filename='prescribed_ismf_adusumilli2020.nc')
63+
self.add_output_file(filename='prescribed_ismf_paolo2023.nc')
6464

6565
self.mesh = mesh
6666

@@ -72,9 +72,9 @@ def run(self):
7272
config = self.config
7373
ntasks = self.ntasks
7474

75-
in_filename = 'Adusumilli_2020_iceshelf_melt_rates_2010-2018_v0.h5'
75+
in_filename = 'Paolo_2023_ANT_G1920V01_IceShelfMelt.nc'
7676

77-
out_filename = 'prescribed_ismf_adusumilli2020.nc'
77+
out_filename = 'prescribed_ismf_paolo2023.nc'
7878

7979
parallel_executable = config.get('parallel', 'parallel_executable')
8080

@@ -84,23 +84,225 @@ def run(self):
8484
map_culled_to_base_filename = 'map_culled_to_base.nc'
8585
mesh_name = self.mesh.mesh_name
8686

87-
remap_adusumilli(
87+
remap_paolo(
8888
in_filename, base_mesh_filename, culled_mesh_filename,
8989
mesh_name, land_ice_mask_filename, out_filename,
9090
logger=logger, mpi_tasks=ntasks,
9191
parallel_executable=parallel_executable,
9292
map_culled_to_base_filename=map_culled_to_base_filename)
9393

9494

95+
def remap_paolo(in_filename, base_mesh_filename, culled_mesh_filename,
96+
mesh_name, land_ice_mask_filename, out_filename, logger,
97+
mapping_directory='.', method='conserve',
98+
renormalization_threshold=None, mpi_tasks=1,
99+
parallel_executable=None,
100+
map_culled_to_base_filename=None):
101+
"""
102+
Remap the Paolo et al. (2023; https://doi.org/10.5194/tc-17-3409-2023)
103+
melt rates at ~2 km resolution to an MPAS mesh
104+
105+
Parameters
106+
----------
107+
in_filename : str
108+
The original Paolo et al. (2023) melt rates
109+
110+
base_mesh_filename : str
111+
The base MPAS mesh before land is culled
112+
113+
culled_mesh_filename : str
114+
The MPAS mesh after land has been culled
115+
116+
mesh_name : str
117+
The name of the mesh (e.g. oEC60to30wISC), used in the name of the
118+
mapping file
119+
120+
land_ice_mask_filename : str
121+
A file containing the variable ``landIceMask`` on the MPAS mesh
122+
123+
out_filename : str
124+
The melt rates interpolated to the MPAS mesh with ocean sensible heat
125+
fluxes added on (assuming insulating ice)
126+
127+
logger : logging.Logger
128+
A logger for output from the step
129+
130+
mapping_directory : str
131+
The directory where the mapping file should be stored (if it is to be
132+
computed) or where it already exists (if not)
133+
134+
method : {'bilinear', 'neareststod', 'conserve'}, optional
135+
The method of interpolation used, see documentation for
136+
`ESMF_RegridWeightGen` for details.
137+
138+
renormalization_threshold : float, optional
139+
The minimum weight of a destination cell after remapping, below
140+
which it is masked out, or ``None`` for no renormalization and
141+
masking.
142+
143+
mpi_tasks : int, optional
144+
The number of MPI tasks to use to compute the mapping file
145+
146+
parallel_executable : {'srun', 'mpirun'}, optional
147+
The name of the parallel executable to use to launch ESMF tools.
148+
But default, 'mpirun' from the conda environment is used
149+
150+
map_culled_to_base_filename : str, optional
151+
A file with indices that map from the culled to the base MPAS mesh. If
152+
not provided, they will be computed
153+
"""
154+
155+
logger.info(f'Reading {in_filename}...')
156+
with xr.open_dataset(in_filename) as ds_in:
157+
158+
x = ds_in.x
159+
y = ds_in.y
160+
melt_rate = ds_in.melt_mean
161+
melt_rate = melt_rate.where(melt_rate.notnull(), 0.)
162+
logger.info('done.')
163+
164+
lx = np.abs(1e-3 * (x[-1] - x[0])).values
165+
ly = np.abs(1e-3 * (y[-1] - y[0])).values
166+
dx = np.abs(1e-3 * (x[1] - x[0])).values
167+
168+
in_grid_name = f'{lx:.1f}x{ly:.1f}km_{dx:.3f}km_Antarctic_stereo'
169+
170+
projection = pyproj.Proj('+proj=stere +lat_ts=-71.0 +lat_0=-90 +lon_0=0.0 '
171+
'+k_0=1.0 +x_0=0.0 +y_0=0.0 +ellps=WGS84')
172+
173+
logger.info('Creating the source grid descriptor...')
174+
in_descriptor = ProjectionGridDescriptor.create(
175+
projection=projection, x=x.values, y=y.values, meshName=in_grid_name)
176+
logger.info('done.')
177+
178+
out_descriptor = MpasCellMeshDescriptor(base_mesh_filename, mesh_name)
179+
180+
mapping_filename = \
181+
f'{mapping_directory}/map_{in_grid_name}_to_{mesh_name}_base.nc'
182+
183+
logger.info(f'Creating the mapping file {mapping_filename}...')
184+
remapper = Remapper(in_descriptor, out_descriptor, mapping_filename)
185+
186+
remapper.build_mapping_file(method=method, mpiTasks=mpi_tasks,
187+
tempdir=mapping_directory, logger=logger,
188+
esmf_parallel_exec=parallel_executable,
189+
include_logs=True)
190+
logger.info('done.')
191+
192+
dx = np.abs(in_descriptor.xCorner[1:] - in_descriptor.xCorner[:-1])
193+
dy = np.abs(in_descriptor.yCorner[1:] - in_descriptor.yCorner[:-1])
194+
dx, dy = np.meshgrid(dx, dy)
195+
planar_area = xr.DataArray(dims=('y', 'x'), data=dx * dy)
196+
197+
with xr.open_dataset(mapping_filename) as ds_map:
198+
earth_radius = constants['SHR_CONST_REARTH']
199+
map_src_area = ds_map.area_a.values * earth_radius**2
200+
map_dst_area = ds_map.area_b.values * earth_radius**2
201+
sphere_area = xr.DataArray(
202+
dims=('y', 'x'), data=map_src_area.reshape(planar_area.shape))
203+
204+
logger.info('Creating the source xarray dataset...')
205+
ds = xr.Dataset()
206+
207+
# convert to the units and variable names expected in MPAS-O
208+
209+
# Paolo et al. (2023) ice density (from attributes)
210+
rho_ice = 917.
211+
s_per_yr = 365. * constants['SHR_CONST_CDAY']
212+
latent_heat_of_fusion = constants['SHR_CONST_LATICE']
213+
ds['x'] = x
214+
ds['y'] = y
215+
area_ratio = planar_area / sphere_area
216+
logger.info(f'min projected area ratio: {area_ratio.min().values}')
217+
logger.info(f'max projected area ratio: {area_ratio.max().values}')
218+
logger.info('')
219+
220+
# original field is negative for melt
221+
fwf = -melt_rate * rho_ice / s_per_yr
222+
field = 'dataLandIceFreshwaterFlux'
223+
ds[field] = area_ratio * fwf
224+
ds[field].attrs['units'] = 'kg m^-2 s^-1'
225+
field = 'dataLandIceHeatFlux'
226+
ds[field] = (latent_heat_of_fusion *
227+
ds.dataLandIceFreshwaterFlux)
228+
ds[field].attrs['units'] = 'W m^-2'
229+
logger.info('Writing the source dataset...')
230+
write_netcdf(ds, 'Paolo_2023_ismf_1992-2017_v1.0.nc')
231+
logger.info('done.')
232+
logger.info('')
233+
234+
planar_flux = (fwf * planar_area).sum().values
235+
sphere_flux = (ds.dataLandIceFreshwaterFlux * sphere_area).sum().values
236+
237+
logger.info(f'Area of a cell (m^2): {planar_area[0,0]:.1f}')
238+
logger.info(f'Total flux on plane (kg/s): {planar_flux:.1f}')
239+
logger.info(f'Total flux on sphere (kg/s): {sphere_flux:.1f}')
240+
logger.info('')
241+
242+
logger.info('Remapping...')
243+
ds_remap = remapper.remap(
244+
ds, renormalizationThreshold=renormalization_threshold)
245+
logger.info('done.')
246+
logger.info('')
247+
248+
with xr.open_dataset(base_mesh_filename) as ds_mesh:
249+
mpas_area_cell = ds_mesh.areaCell
250+
sphere_area_cell = xr.DataArray(
251+
dims=('nCells',), data=map_dst_area)
252+
253+
area_ratio = sphere_area_cell / mpas_area_cell
254+
logger.info(f'min MPAS area ratio: {area_ratio.min().values}')
255+
logger.info(f'max MPAS area ratio: {area_ratio.max().values}')
256+
logger.info('')
257+
258+
sphere_fwf = ds_remap.dataLandIceFreshwaterFlux
259+
260+
field = 'dataLandIceFreshwaterFlux'
261+
ds_remap[field] = area_ratio * sphere_fwf
262+
ds_remap[field].attrs['units'] = 'kg m^-2 s^-1'
263+
field = 'dataLandIceHeatFlux'
264+
ds_remap[field] = area_ratio * ds_remap[field]
265+
ds_remap[field].attrs['units'] = 'W m^-2'
266+
267+
mpas_flux = (ds_remap.dataLandIceFreshwaterFlux *
268+
mpas_area_cell).sum().values
269+
sphere_flux = (sphere_fwf * sphere_area_cell).sum().values
270+
271+
logger.info(f'Total flux w/ MPAS area (kg/s): {mpas_flux:.1f}')
272+
logger.info(f'Total flux w/ sphere area (kg/s): {sphere_flux:.1f}')
273+
274+
if map_culled_to_base_filename is None:
275+
map_culled_to_base_filename = 'map_culled_to_base.nc'
276+
write_map_culled_to_base(base_mesh_filename=base_mesh_filename,
277+
culled_mesh_filename=culled_mesh_filename,
278+
out_filename=map_culled_to_base_filename)
279+
280+
_land_ice_mask_on_base_mesh(
281+
base_mesh_filename=base_mesh_filename,
282+
land_ice_mask_filename=land_ice_mask_filename,
283+
map_culled_to_base_filename=map_culled_to_base_filename)
284+
285+
ds_mask = xr.open_dataset('land_ice_mask_on_base.nc')
286+
mask = ds_mask.landIceFloatingMask
287+
ds_remap['landIceFloatingMask'] = mask
288+
ds_remap.attrs.pop('history')
289+
290+
write_netcdf(ds_remap, 'ismf_remapped_to_base.nc')
291+
292+
# deal with melting beyond the land-ice mask
293+
_reroute_missing_flux(base_mesh_filename, map_culled_to_base_filename,
294+
out_filename, logger)
295+
296+
95297
def remap_adusumilli(in_filename, base_mesh_filename, culled_mesh_filename,
96298
mesh_name, land_ice_mask_filename, out_filename, logger,
97299
mapping_directory='.', method='conserve',
98300
renormalization_threshold=None, mpi_tasks=1,
99301
parallel_executable=None,
100302
map_culled_to_base_filename=None):
101303
"""
102-
Remap the Adusumilli et al. (2020) melt rates at 1 km resolution to an MPAS
103-
mesh
304+
Remap the Adusumilli et al. (2020) melt rates at 0.5 km resolution to an
305+
MPAS mesh
104306
105307
Parameters
106308
----------
@@ -290,11 +492,11 @@ def _reroute_missing_flux(base_mesh_filename, map_culled_to_base_filename,
290492
count = np.sum(reroute_mask.values)
291493
logger.info(f'Rerouting fluxes from {count} cells')
292494
fwf_land_ice = (land_ice_mask * fwf * area).sum().values
293-
logger.info(f'Captured flux (kg/s): {fwf_land_ice:.1f}')
495+
logger.info(f'Captured flux (kg/s): {fwf_land_ice:.1f}')
294496
fwf_rerouted = fw_mass_rerouted.sum().values
295-
logger.info(f'Rerouted flux (kg/s): {fwf_rerouted:.1f}')
497+
logger.info(f'Rerouted flux (kg/s): {fwf_rerouted:.1f}')
296498
fwf_total = (fwf * area).sum().values
297-
logger.info(f'Total flux (kg/s): {fwf_total:.1f}')
499+
logger.info(f'Total flux (kg/s): {fwf_total:.1f}')
298500
ds_to_route = xr.Dataset()
299501
ds_to_route['rerouteMask'] = reroute_mask
300502
write_netcdf(ds_to_route, 'route_mask.nc')
@@ -335,4 +537,5 @@ def _reroute_missing_flux(base_mesh_filename, map_culled_to_base_filename,
335537

336538
fwf_total = (ds_out.dataLandIceFreshwaterFlux *
337539
area.isel(nCells=map_culled_to_base)).sum().values
338-
logger.info(f'Total after rerouting (kg/s): {fwf_total:.1f}')
540+
logger.info(f'Total after rerouting (kg/s): {fwf_total:.1f}')
541+
logger.info('')

docs/developers_guide/ocean/api.rst

+1
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ test cases and steps
215215
init.initial_state.InitialState.run
216216
init.remap_ice_shelf_melt.RemapIceShelfMelt
217217
init.remap_ice_shelf_melt.RemapIceShelfMelt.run
218+
init.remap_ice_shelf_melt.remap_paolo
218219
init.remap_ice_shelf_melt.remap_adusumilli
219220
init.ssh_adjustment.SshAdjustment
220221
init.ssh_adjustment.SshAdjustment.setup

docs/developers_guide/ocean/test_groups/global_ocean.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ ecosystem input data files.
977977

978978
The class :py:class:`compass.ocean.tests.global_ocean.init.remap_ice_shelf_melt.RemapIceShelfMelt`
979979
defines a step that remaps melt rates from the satellite-derived dataset
980-
from `Adusumilli et al. (2020) <https://doi.org/10.1038/s41561-020-0616-z>`_.
980+
from `Paolo et al. (2023) <https://doi.org/10.5194/tc-17-3409-2023>`_.
981981

982982
The class :py:class:`compass.ocean.tests.global_ocean.init.ssh_adjustment.SshAdjustment`
983983
defines a step to adjust the ``landIcePressure`` variable to be in closer to
@@ -1365,7 +1365,7 @@ The test case is made up of 10 steps:
13651365

13661366
:py:class:`compass.ocean.tests.global_ocean.files_for_e3sm.remap_ice_shelf_melt.RemapIceShelfMelt`
13671367
is used to remap ice-shelf melt rates from the dataset of
1368-
`Adusumilli et al. (2020) <https://doi.org/10.1038/s41561-020-0616-z>`_
1368+
`Paolo et al. (2023) <https://doi.org/10.5194/tc-17-3409-2023>`_
13691369
to the MPAS mesh. This dataset is used in E3SM for ``DISMF`` (data
13701370
ice-shelf melt flux) compsets.
13711371

0 commit comments

Comments
 (0)