Skip to content

Commit de137f2

Browse files
committed
Scale fluxes so that total mass is conserved
Since the original mass is on the plane, fluxes must be scaled by the ratio of the area on the plan to the area on the sphere to ensure that the remapped flux has the expected sum. For good measure, this merge also rescales the flux after remapping using the area on the sphere over MPAS's `areaCell` (which are very close).
1 parent df18dfb commit de137f2

File tree

1 file changed

+76
-19
lines changed

1 file changed

+76
-19
lines changed

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

+76-19
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def remap_paolo(in_filename, base_mesh_filename, culled_mesh_filename,
100100
map_culled_to_base_filename=None):
101101
"""
102102
Remap the Paolo et al. (2023; https://doi.org/10.5194/tc-17-3409-2023)
103-
melt rates at 1 km resolution to an MPAS mesh
103+
melt rates at ~2 km resolution to an MPAS mesh
104104
105105
Parameters
106106
----------
@@ -163,8 +163,9 @@ def remap_paolo(in_filename, base_mesh_filename, culled_mesh_filename,
163163

164164
lx = np.abs(1e-3 * (x[-1] - x[0])).values
165165
ly = np.abs(1e-3 * (y[-1] - y[0])).values
166+
dx = np.abs(1e-3 * (x[1] - x[0])).values
166167

167-
in_grid_name = f'{lx}x{ly}km_0.5km_Antarctic_stereo'
168+
in_grid_name = f'{lx:.1f}x{ly:.1f}km_{dx:.3f}km_Antarctic_stereo'
168169

169170
projection = pyproj.Proj('+proj=stere +lat_ts=-71.0 +lat_0=-90 +lon_0=0.0 '
170171
'+k_0=1.0 +x_0=0.0 +y_0=0.0 +ellps=WGS84')
@@ -174,6 +175,32 @@ def remap_paolo(in_filename, base_mesh_filename, culled_mesh_filename,
174175
projection=projection, x=x.values, y=y.values, meshName=in_grid_name)
175176
logger.info('done.')
176177

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+
177204
logger.info('Creating the source xarray dataset...')
178205
ds = xr.Dataset()
179206

@@ -185,9 +212,15 @@ def remap_paolo(in_filename, base_mesh_filename, culled_mesh_filename,
185212
latent_heat_of_fusion = constants['SHR_CONST_LATICE']
186213
ds['x'] = x
187214
ds['y'] = y
188-
field = 'dataLandIceFreshwaterFlux'
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+
189220
# original field is negative for melt
190-
ds[field] = -melt_rate * rho_ice / s_per_yr
221+
fwf = -melt_rate * rho_ice / s_per_yr
222+
field = 'dataLandIceFreshwaterFlux'
223+
ds[field] = area_ratio * fwf
191224
ds[field].attrs['units'] = 'kg m^-2 s^-1'
192225
field = 'dataLandIceHeatFlux'
193226
ds[field] = (latent_heat_of_fusion *
@@ -196,24 +229,47 @@ def remap_paolo(in_filename, base_mesh_filename, culled_mesh_filename,
196229
logger.info('Writing the source dataset...')
197230
write_netcdf(ds, 'Paolo_2023_ismf_1992-2017_v1.0.nc')
198231
logger.info('done.')
232+
logger.info('')
199233

200-
out_descriptor = MpasCellMeshDescriptor(base_mesh_filename, mesh_name)
234+
planar_flux = (fwf * planar_area).sum().values
235+
sphere_flux = (ds.dataLandIceFreshwaterFlux * sphere_area).sum().values
201236

202-
mapping_filename = \
203-
f'{mapping_directory}/map_{in_grid_name}_to_{mesh_name}_base.nc'
204-
205-
logger.info(f'Creating the mapping file {mapping_filename}...')
206-
remapper = Remapper(in_descriptor, out_descriptor, mapping_filename)
207-
208-
remapper.build_mapping_file(method=method, mpiTasks=mpi_tasks,
209-
tempdir=mapping_directory, logger=logger,
210-
esmf_parallel_exec=parallel_executable)
211-
logger.info('done.')
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('')
212241

213242
logger.info('Remapping...')
214243
ds_remap = remapper.remap(
215244
ds, renormalizationThreshold=renormalization_threshold)
216245
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}')
217273

218274
if map_culled_to_base_filename is None:
219275
map_culled_to_base_filename = 'map_culled_to_base.nc'
@@ -436,11 +492,11 @@ def _reroute_missing_flux(base_mesh_filename, map_culled_to_base_filename,
436492
count = np.sum(reroute_mask.values)
437493
logger.info(f'Rerouting fluxes from {count} cells')
438494
fwf_land_ice = (land_ice_mask * fwf * area).sum().values
439-
logger.info(f'Captured flux (kg/s): {fwf_land_ice:.1f}')
495+
logger.info(f'Captured flux (kg/s): {fwf_land_ice:.1f}')
440496
fwf_rerouted = fw_mass_rerouted.sum().values
441-
logger.info(f'Rerouted flux (kg/s): {fwf_rerouted:.1f}')
497+
logger.info(f'Rerouted flux (kg/s): {fwf_rerouted:.1f}')
442498
fwf_total = (fwf * area).sum().values
443-
logger.info(f'Total flux (kg/s): {fwf_total:.1f}')
499+
logger.info(f'Total flux (kg/s): {fwf_total:.1f}')
444500
ds_to_route = xr.Dataset()
445501
ds_to_route['rerouteMask'] = reroute_mask
446502
write_netcdf(ds_to_route, 'route_mask.nc')
@@ -481,4 +537,5 @@ def _reroute_missing_flux(base_mesh_filename, map_culled_to_base_filename,
481537

482538
fwf_total = (ds_out.dataLandIceFreshwaterFlux *
483539
area.isel(nCells=map_culled_to_base)).sum().values
484-
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('')

0 commit comments

Comments
 (0)