@@ -31,7 +31,7 @@ def __init__(self, test_case, mesh):
31
31
32
32
mesh : compass.ocean.tests.global_ocean.mesh.Mesh
33
33
The test case that produces the mesh for this run
34
- """ # noqa: E501
34
+ """
35
35
super ().__init__ (test_case , name = 'remap_ice_shelf_melt' , ntasks = 512 ,
36
36
min_tasks = 1 )
37
37
@@ -55,12 +55,12 @@ def __init__(self, test_case, mesh):
55
55
work_dir_target = f'{ culled_mesh_path } /land_ice_mask.nc' )
56
56
57
57
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 ' ,
60
60
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
62
62
63
- self .add_output_file (filename = 'prescribed_ismf_adusumilli2020 .nc' )
63
+ self .add_output_file (filename = 'prescribed_ismf_paolo2023 .nc' )
64
64
65
65
self .mesh = mesh
66
66
@@ -72,9 +72,9 @@ def run(self):
72
72
config = self .config
73
73
ntasks = self .ntasks
74
74
75
- in_filename = 'Adusumilli_2020_iceshelf_melt_rates_2010-2018_v0.h5 '
75
+ in_filename = 'Paolo_2023_ANT_G1920V01_IceShelfMelt.nc '
76
76
77
- out_filename = 'prescribed_ismf_adusumilli2020 .nc'
77
+ out_filename = 'prescribed_ismf_paolo2023 .nc'
78
78
79
79
parallel_executable = config .get ('parallel' , 'parallel_executable' )
80
80
@@ -84,23 +84,225 @@ def run(self):
84
84
map_culled_to_base_filename = 'map_culled_to_base.nc'
85
85
mesh_name = self .mesh .mesh_name
86
86
87
- remap_adusumilli (
87
+ remap_paolo (
88
88
in_filename , base_mesh_filename , culled_mesh_filename ,
89
89
mesh_name , land_ice_mask_filename , out_filename ,
90
90
logger = logger , mpi_tasks = ntasks ,
91
91
parallel_executable = parallel_executable ,
92
92
map_culled_to_base_filename = map_culled_to_base_filename )
93
93
94
94
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
+
95
297
def remap_adusumilli (in_filename , base_mesh_filename , culled_mesh_filename ,
96
298
mesh_name , land_ice_mask_filename , out_filename , logger ,
97
299
mapping_directory = '.' , method = 'conserve' ,
98
300
renormalization_threshold = None , mpi_tasks = 1 ,
99
301
parallel_executable = None ,
100
302
map_culled_to_base_filename = None ):
101
303
"""
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
104
306
105
307
Parameters
106
308
----------
@@ -290,11 +492,11 @@ def _reroute_missing_flux(base_mesh_filename, map_culled_to_base_filename,
290
492
count = np .sum (reroute_mask .values )
291
493
logger .info (f'Rerouting fluxes from { count } cells' )
292
494
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} ' )
294
496
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} ' )
296
498
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} ' )
298
500
ds_to_route = xr .Dataset ()
299
501
ds_to_route ['rerouteMask' ] = reroute_mask
300
502
write_netcdf (ds_to_route , 'route_mask.nc' )
@@ -335,4 +537,5 @@ def _reroute_missing_flux(base_mesh_filename, map_culled_to_base_filename,
335
537
336
538
fwf_total = (ds_out .dataLandIceFreshwaterFlux *
337
539
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 ('' )
0 commit comments