Skip to content

Commit 0418b56

Browse files
Merge pull request #937 from trhille/extended_gris_mesh
Reduce number of empty cells in extended high-resolution Greenland Ice Sheet mesh This merge includes changes needed for a new high-resolution (1–10 km) extended Greenland mesh with variable mesh resolution in the ocean and bare land. The goal is to have high enough resolution in the ocean to resolve fjords for coupling with the ocean, but to use lower resolution where possible to reduce wasted cells. The extended 1–10km without these changes contains 1.4 million cells, only ~350k of which contain ice. With these changes, the extended 1–10km mesh is 870k cells.
2 parents b734eb0 + 2eefef6 commit 0418b56

7 files changed

Lines changed: 268 additions & 66 deletions

File tree

compass/landice/mesh.py

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,9 @@ def clip_mesh_to_bounding_box(mask_ds, base_ds, bounding_box):
224224
return mask_ds
225225

226226

227-
def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None,
227+
def set_cell_width(self, section_name, thk, bed, vx=None, vy=None,
228228
dist_to_edge=None, dist_to_grounding_line=None,
229+
dist_to_coast=None,
229230
flood_fill_iStart=None, flood_fill_jStart=None):
230231
"""
231232
Set cell widths based on settings in config file to pass to
@@ -238,9 +239,10 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None,
238239
following options to be set in the given config section:
239240
``levels``, ``x_min``, ``x_max``, ``y_min``, ``y_max``,
240241
``min_spac``, ``max_spac``, ``high_log_speed``, ``low_log_speed``,
241-
``high_dist``, ``low_dist``, ``high_dist_bed``, ``low_dist_bed``,
242-
``high_bed``, ``low_bed``, ``cull_distance``, ``use_speed``,
243-
``use_dist_to_edge``, ``use_dist_to_grounding_line``, and ``use_bed``.
242+
``high_dist``, ``low_dist``, ``high_dist_coast``, ``low_dist_coast``,
243+
``high_dist_bed``, ``low_dist_bed``, ``high_bed``, ``low_bed``,
244+
``cull_distance``, ``use_speed``, ``use_dist_to_edge``,
245+
``use_dist_to_grounding_line``, ``use_dist_to_coast``, and ``use_bed``.
244246
See the Land-Ice Framework section of the Users or Developers guide
245247
for more information about these options and their uses.
246248
@@ -272,6 +274,11 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None,
272274
function. Can be set to ``None`` if
273275
``use_dist_to_grounding_line == False`` in config file.
274276
277+
dist_to_coast : numpy.ndarray, optional
278+
Distance from each cell to coast, calculated in separate
279+
function. Can be set to ``None`` if
280+
``use_dist_to_coast == False`` in config file.
281+
275282
flood_fill_iStart : int, optional
276283
x-index location to start flood-fill when using bed topography
277284
@@ -291,21 +298,19 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None,
291298
# Get config inputs for cell spacing functions
292299
min_spac = section.getfloat('min_spac')
293300
max_spac = section.getfloat('max_spac')
294-
high_log_speed = section.getfloat('high_log_speed')
295-
low_log_speed = section.getfloat('low_log_speed')
296-
high_dist = section.getfloat('high_dist')
297-
low_dist = section.getfloat('low_dist')
298-
high_dist_bed = section.getfloat('high_dist_bed')
299-
low_dist_bed = section.getfloat('low_dist_bed')
300-
low_bed = section.getfloat('low_bed')
301-
high_bed = section.getfloat('high_bed')
302-
303301
# convert km to m
304302
cull_distance = section.getfloat('cull_distance') * 1.e3
305303

304+
land_mask = np.logical_and(thk == 0.0, bed >= 0.)
305+
ocean_mask = np.logical_and(thk == 0.0, bed < 0.)
306+
306307
# Cell spacing function based on union of masks
307308
if section.get('use_bed') == 'True':
308309
logger.info('Using bed elevation for spacing.')
310+
high_dist_bed = section.getfloat('high_dist_bed')
311+
low_dist_bed = section.getfloat('low_dist_bed')
312+
low_bed = section.getfloat('low_bed')
313+
high_bed = section.getfloat('high_bed')
309314
if flood_fill_iStart is not None and flood_fill_jStart is not None:
310315
logger.info('calling gridded_flood_fill to find ' +
311316
'bedTopography <= low_bed connected to the ocean.')
@@ -355,6 +360,8 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None,
355360
# Make cell spacing function mapping from log speed to cell spacing
356361
if section.get('use_speed') == 'True':
357362
logger.info('Using speed for cell spacing')
363+
high_log_speed = section.getfloat('high_log_speed')
364+
low_log_speed = section.getfloat('low_log_speed')
358365
speed = (vx ** 2 + vy ** 2) ** 0.5
359366
lspd = np.log10(speed)
360367
spacing_speed = np.interp(lspd, [low_log_speed, high_log_speed],
@@ -372,33 +379,53 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None,
372379
f'dataset with missing velocity values. Setting '
373380
f'velocity-based spacing to maximum value.')
374381

375-
spacing_speed[thk == 0.0] = min_spac
376382
else:
377383
spacing_speed = max_spac * np.ones_like(thk)
378384

379385
# Make cell spacing function mapping from distance to ice edge
380386
if section.get('use_dist_to_edge') == 'True':
381387
logger.info('Using distance to ice edge for cell spacing')
388+
high_dist = section.getfloat('high_dist')
389+
low_dist = section.getfloat('low_dist')
382390
spacing_edge = np.interp(dist_to_edge, [low_dist, high_dist],
383391
[min_spac, max_spac], left=min_spac,
384392
right=max_spac)
385-
spacing_edge[thk == 0.0] = min_spac
386393
else:
387394
spacing_edge = max_spac * np.ones_like(thk)
388395

389396
# Make cell spacing function mapping from distance to grounding line
390397
if section.get('use_dist_to_grounding_line') == 'True':
391398
logger.info('Using distance to grounding line for cell spacing')
399+
high_dist = section.getfloat('high_dist')
400+
low_dist = section.getfloat('low_dist')
392401
spacing_gl = np.interp(dist_to_grounding_line, [low_dist, high_dist],
393402
[min_spac, max_spac], left=min_spac,
394403
right=max_spac)
395-
spacing_gl[thk == 0.0] = min_spac
396404
else:
397405
spacing_gl = max_spac * np.ones_like(thk)
398406

407+
# Make cell spacing function mapping from distance to coast
408+
if section.get('use_dist_to_coast') == 'True':
409+
logger.info('Using distance to coast for cell spacing')
410+
high_dist_coast = section.getfloat('high_dist_coast')
411+
low_dist_coast = section.getfloat('low_dist_coast')
412+
spacing_coast = np.interp(dist_to_coast,
413+
[low_dist_coast, high_dist_coast],
414+
[min_spac, max_spac], left=min_spac,
415+
right=max_spac)
416+
# distance from coast is only used to set spacing in ocean
417+
spacing_coast[bed > 0.] = max_spac
418+
else:
419+
spacing_coast = max_spac * np.ones_like(thk)
420+
# If not using distance from coast, use finest spacing
421+
# in the ocean as well as ice-free land.
422+
spacing_coast[land_mask] = min_spac
423+
spacing_coast[ocean_mask] = min_spac
424+
399425
# Merge cell spacing methods
400426
cell_width = max_spac * np.ones_like(thk)
401-
for width in [spacing_bed, spacing_speed, spacing_edge, spacing_gl]:
427+
for width in [spacing_bed, spacing_speed, spacing_edge,
428+
spacing_gl, spacing_coast]:
402429
cell_width = np.minimum(cell_width, width)
403430

404431
# Set large cell_width in areas we are going to cull anyway (speeds up
@@ -420,8 +447,7 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name,
420447
"""
421448
Calculate distance from each point to ice edge and grounding line,
422449
to be used in mesh density functions in
423-
:py:func:`compass.landice.mesh.set_cell_width()`. In future development,
424-
this should be updated to use a faster package such as ``scikit-fmm``.
450+
:py:func:`compass.landice.mesh.set_cell_width()`.
425451
426452
Parameters
427453
----------
@@ -465,6 +491,11 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name,
465491
466492
dist_to_grounding_line : numpy.ndarray
467493
Distance from each cell to the grounding line
494+
495+
dist_to_coast : numpy.ndarray
496+
Distance from each cell to the coast, defined as the last cell
497+
adjacent to ocean (ice free with bed < 0) whether ice-filled
498+
or ice-free.
468499
"""
469500

470501
logger = self.logger
@@ -495,12 +526,14 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name,
495526
])
496527

497528
ice_mask = thk > 0.0
529+
ocean_mask = np.logical_and(thk == 0., topg < 0.)
498530
grounded_mask = np.logical_and(thk > (-1028.0 / 910.0 * topg),
499531
ice_mask)
500532
float_mask = np.logical_and(thk < (-1028.0 / 910.0 * topg),
501533
ice_mask)
502534
margin_mask = np.zeros(thk.shape, dtype=bool)
503535
grounding_line_mask = np.zeros(thk.shape, dtype=bool)
536+
coast_mask = np.zeros(thk.shape, dtype=bool)
504537

505538
for n in neighbors:
506539
shifted_ice = np.roll(ice_mask, n, axis=[0, 1])
@@ -512,17 +545,24 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name,
512545
grounding_line_mask = np.logical_or(grounding_line_mask,
513546
not_grounded_mask)
514547

548+
shifted_ocean = np.roll(ocean_mask, n, axis=[0, 1])
549+
coast_mask = np.logical_or(coast_mask, shifted_ocean)
550+
515551
# where ice exists and neighbors non-ice locations
516552
margin_mask = np.logical_and(margin_mask, ice_mask)
517553
# where grounded ice exists and neighbors floating ice
518554
grounding_line_mask = np.logical_and(grounding_line_mask, grounded_mask)
555+
# where non-ocean exists and neighbors ocean locations
556+
coast_mask = np.logical_and(coast_mask, np.logical_not(ocean_mask))
519557

520-
fig, ax = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(6, 3))
558+
fig, ax = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(9, 3))
521559
margin_plot = ax[0].pcolor(margin_mask)
522560
gl_plot = ax[1].pcolor(grounding_line_mask) # noqa F841
561+
coast_plot = ax[2].pcolor(coast_mask) # noqa F841
523562
ax[0].set_title("margin mask")
524563
ax[1].set_title("grounding line mask")
525-
plt.colorbar(margin_plot, ax=[ax[0], ax[1]], shrink=0.7)
564+
ax[2].set_title("coast mask")
565+
plt.colorbar(margin_plot, ax=ax, shrink=0.7)
526566
[ax.set_aspect('equal') for ax in ax]
527567
fig.savefig("masks.png", dpi=400)
528568

@@ -534,19 +574,23 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name,
534574
dist_to_grounding_line = distance_transform_edt(
535575
~grounding_line_mask, sampling=(dy, dx)
536576
)
577+
dist_to_coast = distance_transform_edt(
578+
~coast_mask, sampling=(dy, dx)
579+
)
537580

538581
# Limit maximum distance
539582
max_dist = float(np.ceil(window_size / max(dx, dy))) * max(dx, dy)
540583
dist_to_edge = np.minimum(dist_to_edge, max_dist)
541584
dist_to_grounding_line = np.minimum(dist_to_grounding_line, max_dist)
585+
dist_to_coast = np.minimum(dist_to_coast, max_dist)
542586

543587
toc = time.time()
544588
logger.info(
545589
'compass.landice.mesh.get_dist_to_edge_and_gl() took '
546590
f'{toc - tic:0.2f} seconds'
547591
)
548592

549-
return dist_to_edge, dist_to_grounding_line
593+
return dist_to_edge, dist_to_grounding_line, dist_to_coast
550594

551595

552596
def build_cell_width(self, section_name, gridded_dataset,
@@ -628,7 +672,7 @@ def build_cell_width(self, section_name, gridded_dataset,
628672

629673
# Calculate distance from each grid point to ice edge
630674
# and grounding line, for use in cell spacing functions.
631-
distToEdge, distToGL = get_dist_to_edge_and_gl(
675+
distToEdge, distToGL, distToCoast = get_dist_to_edge_and_gl(
632676
self, thk, topg, x1,
633677
y1, section_name=section_name)
634678

@@ -637,6 +681,7 @@ def build_cell_width(self, section_name, gridded_dataset,
637681
thk=thk, bed=topg, vx=vx, vy=vy,
638682
dist_to_edge=distToEdge,
639683
dist_to_grounding_line=distToGL,
684+
dist_to_coast=distToCoast,
640685
flood_fill_iStart=flood_fill_start[0],
641686
flood_fill_jStart=flood_fill_start[1])
642687

@@ -683,9 +728,10 @@ def build_mali_mesh(self, cell_width, x1, y1, geom_points,
683728
following options to be set in the given config section:
684729
``levels``, ``x_min``, ``x_max``, ``y_min``, ``y_max``,
685730
``min_spac``, ``max_spac``, ``high_log_speed``, ``low_log_speed``,
686-
``high_dist``, ``low_dist``, ``high_dist_bed``, ``low_dist_bed``,
687-
``high_bed``, ``low_bed``, ``cull_distance``, ``use_speed``,
688-
``use_dist_to_edge``, ``use_dist_to_grounding_line``, and ``use_bed``.
731+
``high_dist``, ``low_dist``, ``high_dist_coast``, ``low_dist_coast``,
732+
``high_dist_bed``, ``low_dist_bed``, ``high_bed``, ``low_bed``,
733+
``cull_distance``, ``use_speed``, ``use_dist_to_edge``,
734+
``use_dist_to_grounding_line``, ``use_dist_to_coast``, and ``use_bed``.
689735
See the Land-Ice Framework section of the Users or Developers guide
690736
for more information about these options and their uses.
691737

compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ y_max = None
1515
# distance from ice margin to cull (km).
1616
# Set to a value <= 0 if you do not want
1717
# to cull based on distance from margin.
18-
cull_distance = 10.0
18+
cull_distance = -100.0
1919

2020
# mesh density parameters
2121
# minimum cell spacing (meters)
22-
min_spac = 3.e3
22+
min_spac = 4.e3
2323
# maximum cell spacing (meters)
24-
max_spac = 30.e3
24+
max_spac = 40.e3
2525
# log10 of max speed for cell spacing
2626
high_log_speed = 2.5
2727
# log10 of min speed for cell spacing
@@ -30,6 +30,10 @@ low_log_speed = 0.75
3030
high_dist = 1.e5
3131
# distance within which cell spacing = min_spac
3232
low_dist = 1.e4
33+
# distance at which cell spacing in ocean = max_spac
34+
high_dist_coast = 16.e3
35+
# distance within which cell spacing in ocean = min_spac
36+
low_dist_coast = 8.e3
3337
# distance at which bed topography has no effect
3438
high_dist_bed = 1.e5
3539
# distance within which bed topography has maximum effect
@@ -43,6 +47,7 @@ high_bed = 100.0
4347
use_speed = True
4448
use_dist_to_grounding_line = False
4549
use_dist_to_edge = True
50+
use_dist_to_coast = True
4651
use_bed = True
4752

4853
[greenland]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# config options for high_res_mesh test case
2+
[mesh]
3+
4+
# number of levels in the mesh
5+
levels = 10
6+
7+
# Bounds of GIS mesh. If any bound is set
8+
# to None, the mesh will use the full extent
9+
# of the gridded dataset.
10+
x_min = None
11+
x_max = None
12+
y_min = None
13+
y_max = None
14+
15+
# distance from ice margin to cull (km).
16+
# Set to a value <= 0 if you do not want
17+
# to cull based on distance from margin.
18+
cull_distance = -100.0
19+
20+
# mesh density parameters
21+
# minimum cell spacing (meters)
22+
min_spac = 1.e3
23+
# maximum cell spacing (meters)
24+
max_spac = 10.e3
25+
# log10 of max speed for cell spacing
26+
high_log_speed = 2.5
27+
# log10 of min speed for cell spacing
28+
low_log_speed = 0.75
29+
# distance at which cell spacing = max_spac
30+
high_dist = 1.e5
31+
# distance within which cell spacing = min_spac
32+
low_dist = 1.e4
33+
# distance at which cell spacing in ocean = max_spac
34+
high_dist_coast = 10.e3
35+
# distance within which cell spacing in ocean = min_spac
36+
low_dist_coast = 2.e3
37+
# distance at which bed topography has no effect
38+
high_dist_bed = 1.e5
39+
# distance within which bed topography has maximum effect
40+
low_dist_bed = 7.5e4
41+
# Bed elev beneath which cell spacing is minimized
42+
low_bed = 50.0
43+
# Bed elev above which cell spacing is maximized
44+
high_bed = 100.0
45+
46+
# mesh density functions
47+
use_speed = True
48+
use_dist_to_grounding_line = False
49+
use_dist_to_edge = True
50+
use_dist_to_coast = True
51+
use_bed = True
52+
53+
[greenland]
54+
# Whether to interpolate data (controls run_optional_interpolation)
55+
interpolate_data = True
56+
# path to directory containing BedMachine and Measures datasets
57+
# (default value is for Perlmutter)
58+
data_path = /global/cfs/cdirs/fanssie/standard_datasets/GIS_datasets/
59+
60+
# filename of the BedMachine thickness and bedTopography dataset
61+
# (default value is for Perlmutter)
62+
bedmachine_filename = BedMachineGreenland-v6_edits_floodFill_extrap.nc
63+
64+
# filename of the MEaSUREs ice velocity dataset
65+
# (default value is for Perlmutter)
66+
measures_filename = greenland_vel_mosaic500_extrap.nc
67+
68+
# projection of the source datasets, according to the dictionary keys
69+
# create_scrip_file_from_planar_rectangular_grid from MPAS_Tools
70+
src_proj = gis-gimp
71+
72+
# number of processors to use for ESMF_RegridWeightGen
73+
nProcs = 2048

0 commit comments

Comments
 (0)