From 3cab0b59e90970043972a4184489afc865b44f8b Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Wed, 25 Feb 2026 20:24:56 -0800 Subject: [PATCH 01/12] Add option to set resolution in ocean based on distance from coast Add option to set resolution in ocean based on distance from coast. --- compass/landice/mesh.py | 76 +++++++++++++++---- .../tests/greenland/mesh_gen/mesh_gen.cfg | 5 ++ 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/compass/landice/mesh.py b/compass/landice/mesh.py index 5718568521..8652000e66 100644 --- a/compass/landice/mesh.py +++ b/compass/landice/mesh.py @@ -226,6 +226,7 @@ def clip_mesh_to_bounding_box(mask_ds, base_ds, bounding_box): def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, dist_to_edge=None, dist_to_grounding_line=None, + dist_to_coast=None, flood_fill_iStart=None, flood_fill_jStart=None): """ 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, following options to be set in the given config section: ``levels``, ``x_min``, ``x_max``, ``y_min``, ``y_max``, ``min_spac``, ``max_spac``, ``high_log_speed``, ``low_log_speed``, - ``high_dist``, ``low_dist``, ``high_dist_bed``, ``low_dist_bed``, - ``high_bed``, ``low_bed``, ``cull_distance``, ``use_speed``, - ``use_dist_to_edge``, ``use_dist_to_grounding_line``, and ``use_bed``. + ``high_dist``, ``low_dist``, ``high_dist_coast``, ``low_dist_coast``, + ``high_dist_bed``, ``low_dist_bed``, ``high_bed``, ``low_bed``, + ``cull_distance``, ``use_speed``, ``use_dist_to_edge``, + ``use_dist_to_grounding_line``, ``use_dist_to_coast``, and ``use_bed``. See the Land-Ice Framework section of the Users or Developers guide for more information about these options and their uses. @@ -272,6 +274,11 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, function. Can be set to ``None`` if ``use_dist_to_grounding_line == False`` in config file. + dist_to_coast : numpy.ndarray, optional + Distance from each cell to coast, calculated in separate + function. Can be set to ``None`` if + ``use_dist_to_coast == False`` in config file. + flood_fill_iStart : int, optional x-index location to start flood-fill when using bed topography @@ -295,6 +302,8 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, low_log_speed = section.getfloat('low_log_speed') high_dist = section.getfloat('high_dist') low_dist = section.getfloat('low_dist') + high_dist_coast = section.getfloat('high_dist_coast') + low_dist_coast = section.getfloat('low_dist_coast') high_dist_bed = section.getfloat('high_dist_bed') low_dist_bed = section.getfloat('low_dist_bed') low_bed = section.getfloat('low_bed') @@ -303,6 +312,9 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, # convert km to m cull_distance = section.getfloat('cull_distance') * 1.e3 + land_mask = np.logical_and(thk == 0.0, bed >= 0.) + ocean_mask = np.logical_and(thk == 0.0, bed < 0.) + # Cell spacing function based on union of masks if section.get('use_bed') == 'True': logger.info('Using bed elevation for spacing.') @@ -372,7 +384,7 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, f'dataset with missing velocity values. Setting ' f'velocity-based spacing to maximum value.') - spacing_speed[thk == 0.0] = min_spac + spacing_speed[land_mask] = min_spac else: spacing_speed = max_spac * np.ones_like(thk) @@ -382,7 +394,7 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, spacing_edge = np.interp(dist_to_edge, [low_dist, high_dist], [min_spac, max_spac], left=min_spac, right=max_spac) - spacing_edge[thk == 0.0] = min_spac + spacing_edge[land_mask] = min_spac else: spacing_edge = max_spac * np.ones_like(thk) @@ -392,13 +404,30 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, spacing_gl = np.interp(dist_to_grounding_line, [low_dist, high_dist], [min_spac, max_spac], left=min_spac, right=max_spac) - spacing_gl[thk == 0.0] = min_spac + spacing_gl[land_mask] = min_spac else: spacing_gl = max_spac * np.ones_like(thk) + # Make cell spacing function mapping from distance to coast + if section.get('use_dist_to_coast') == 'True': + logger.info('Using distance to coast for cell spacing') + spacing_coast = np.interp(dist_to_coast, + [low_dist_coast, high_dist_coast], + [min_spac, max_spac], left=min_spac, + right=max_spac) + # distance from coast is only used to set spacing in ocean + spacing_coast[bed > 0.] = max_spac + else: + spacing_coast = max_spac * np.ones_like(thk) + # If not using distance from coast, use finest spacing + # in the ocean as well as ice-free land. + spacing_coast[land_mask] = min_spac + spacing_coast[ocean_mask] = min_spac + # Merge cell spacing methods cell_width = max_spac * np.ones_like(thk) - for width in [spacing_bed, spacing_speed, spacing_edge, spacing_gl]: + for width in [spacing_bed, spacing_speed, spacing_edge, + spacing_gl, spacing_coast]: cell_width = np.minimum(cell_width, width) # Set large cell_width in areas we are going to cull anyway (speeds up @@ -465,6 +494,11 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name, dist_to_grounding_line : numpy.ndarray Distance from each cell to the grounding line + + dist_to_coast : numpy.ndarray + Distance from each cell to the coast, defined as the last cell + adjacent to ocean (ice free with bed < 0) whether ice-filled + or ice-free. """ logger = self.logger @@ -495,12 +529,14 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name, ]) ice_mask = thk > 0.0 + ocean_mask = np.logical_and(thk == 0., topg < 0.) grounded_mask = np.logical_and(thk > (-1028.0 / 910.0 * topg), ice_mask) float_mask = np.logical_and(thk < (-1028.0 / 910.0 * topg), ice_mask) margin_mask = np.zeros(thk.shape, dtype=bool) grounding_line_mask = np.zeros(thk.shape, dtype=bool) + coast_mask = np.zeros(thk.shape, dtype=bool) for n in neighbors: shifted_ice = np.roll(ice_mask, n, axis=[0, 1]) @@ -512,16 +548,24 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name, grounding_line_mask = np.logical_or(grounding_line_mask, not_grounded_mask) + not_ocean_mask = np.logical_not(np.roll(ocean_mask, n, axis=[0, 1])) + coast_mask = np.logical_or(coast_mask, not_ocean_mask) + # where ice exists and neighbors non-ice locations margin_mask = np.logical_and(margin_mask, ice_mask) # where grounded ice exists and neighbors floating ice grounding_line_mask = np.logical_and(grounding_line_mask, grounded_mask) + coast_mask = np.logical_and( + coast_mask, + np.logical_or(ice_mask, ~ocean_mask)) - fig, ax = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(6, 3)) + fig, ax = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(9, 3)) margin_plot = ax[0].pcolor(margin_mask) gl_plot = ax[1].pcolor(grounding_line_mask) # noqa F841 + coast_plot = ax[2].pcolor(coast_mask) ax[0].set_title("margin mask") ax[1].set_title("grounding line mask") + ax[2].set_title("coast mask") plt.colorbar(margin_plot, ax=[ax[0], ax[1]], shrink=0.7) [ax.set_aspect('equal') for ax in ax] fig.savefig("masks.png", dpi=400) @@ -534,11 +578,15 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name, dist_to_grounding_line = distance_transform_edt( ~grounding_line_mask, sampling=(dy, dx) ) + dist_to_coast = distance_transform_edt( + ~coast_mask, sampling=(dy, dx) + ) # Limit maximum distance max_dist = float(np.ceil(window_size / max(dx, dy))) * max(dx, dy) dist_to_edge = np.minimum(dist_to_edge, max_dist) dist_to_grounding_line = np.minimum(dist_to_grounding_line, max_dist) + dist_to_coast = np.minimum(dist_to_coast, max_dist) toc = time.time() logger.info( @@ -546,7 +594,7 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name, f'{toc - tic:0.2f} seconds' ) - return dist_to_edge, dist_to_grounding_line + return dist_to_edge, dist_to_grounding_line, dist_to_coast def build_cell_width(self, section_name, gridded_dataset, @@ -628,7 +676,7 @@ def build_cell_width(self, section_name, gridded_dataset, # Calculate distance from each grid point to ice edge # and grounding line, for use in cell spacing functions. - distToEdge, distToGL = get_dist_to_edge_and_gl( + distToEdge, distToGL, distToCoast = get_dist_to_edge_and_gl( self, thk, topg, x1, y1, section_name=section_name) @@ -637,6 +685,7 @@ def build_cell_width(self, section_name, gridded_dataset, thk=thk, bed=topg, vx=vx, vy=vy, dist_to_edge=distToEdge, dist_to_grounding_line=distToGL, + dist_to_coast=distToCoast, flood_fill_iStart=flood_fill_start[0], flood_fill_jStart=flood_fill_start[1]) @@ -683,9 +732,10 @@ def build_mali_mesh(self, cell_width, x1, y1, geom_points, following options to be set in the given config section: ``levels``, ``x_min``, ``x_max``, ``y_min``, ``y_max``, ``min_spac``, ``max_spac``, ``high_log_speed``, ``low_log_speed``, - ``high_dist``, ``low_dist``, ``high_dist_bed``, ``low_dist_bed``, - ``high_bed``, ``low_bed``, ``cull_distance``, ``use_speed``, - ``use_dist_to_edge``, ``use_dist_to_grounding_line``, and ``use_bed``. + ``high_dist``, ``low_dist``, ``high_dist_coast``, ``low_dist_coast``, + ``high_dist_bed``, ``low_dist_bed``, ``high_bed``, ``low_bed``, + ``cull_distance``, ``use_speed``, ``use_dist_to_edge``, + ``use_dist_to_grounding_line``, ``use_dist_to_coast``, and ``use_bed``. See the Land-Ice Framework section of the Users or Developers guide for more information about these options and their uses. diff --git a/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg b/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg index c1b7250a50..c3471d6c3b 100644 --- a/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg +++ b/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg @@ -30,6 +30,10 @@ low_log_speed = 0.75 high_dist = 1.e5 # distance within which cell spacing = min_spac low_dist = 1.e4 +# distance at which cell spacing in ocean = max_spac +high_dist_coast = 16.e3 +# distance within which cell spaceing in ocean = min_spac +low_dist_coast = 8.e3 # distance at which bed topography has no effect high_dist_bed = 1.e5 # distance within which bed topography has maximum effect @@ -43,6 +47,7 @@ high_bed = 100.0 use_speed = True use_dist_to_grounding_line = False use_dist_to_edge = True +use_dist_to_coast = True use_bed = True [greenland] From 75f825619a9f34e35b44e91853e347720a8ddeb9 Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Thu, 26 Feb 2026 19:47:22 -0800 Subject: [PATCH 02/12] Fix coast mask definition Fix coast mask definition --- compass/landice/mesh.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compass/landice/mesh.py b/compass/landice/mesh.py index 8652000e66..a9ef08ee2a 100644 --- a/compass/landice/mesh.py +++ b/compass/landice/mesh.py @@ -555,9 +555,7 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name, margin_mask = np.logical_and(margin_mask, ice_mask) # where grounded ice exists and neighbors floating ice grounding_line_mask = np.logical_and(grounding_line_mask, grounded_mask) - coast_mask = np.logical_and( - coast_mask, - np.logical_or(ice_mask, ~ocean_mask)) + coast_mask = np.logical_and(coast_mask, ocean_mask) fig, ax = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(9, 3)) margin_plot = ax[0].pcolor(margin_mask) From c942ce41ed63a8e66ff8a3f2dade8cef471fa8b4 Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Thu, 26 Feb 2026 20:47:45 -0800 Subject: [PATCH 03/12] Do not enforce maximum resolution on bare land Remove constraint that forces maximum resolution everywhere on bare land, which leads to enormous numbers of wasted cells in Greenland. --- compass/landice/mesh.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/compass/landice/mesh.py b/compass/landice/mesh.py index a9ef08ee2a..6e3fdf51f7 100644 --- a/compass/landice/mesh.py +++ b/compass/landice/mesh.py @@ -384,7 +384,6 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, f'dataset with missing velocity values. Setting ' f'velocity-based spacing to maximum value.') - spacing_speed[land_mask] = min_spac else: spacing_speed = max_spac * np.ones_like(thk) @@ -394,7 +393,6 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, spacing_edge = np.interp(dist_to_edge, [low_dist, high_dist], [min_spac, max_spac], left=min_spac, right=max_spac) - spacing_edge[land_mask] = min_spac else: spacing_edge = max_spac * np.ones_like(thk) @@ -404,7 +402,6 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, spacing_gl = np.interp(dist_to_grounding_line, [low_dist, high_dist], [min_spac, max_spac], left=min_spac, right=max_spac) - spacing_gl[land_mask] = min_spac else: spacing_gl = max_spac * np.ones_like(thk) From a02af8db6a81efed9ab1414b645e20ffbfbe592e Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Fri, 27 Feb 2026 09:25:52 -0800 Subject: [PATCH 04/12] =?UTF-8?q?Create=20separate=20.cfg=20file=20for=201?= =?UTF-8?q?=E2=80=9310km=20Greenland=20mesh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a separate .cfg file for the 1–10km Greenland mesh, and change the default resolution from 3–30km to 4–40km to avoid out-of-memory errors when running ESMF_RegridWeightGen on a single Perlmutter node. --- .../tests/greenland/mesh_gen/mesh_gen.cfg | 6 +- .../greenland/mesh_gen/mesh_gen_1to10km.cfg | 74 +++++++++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg diff --git a/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg b/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg index c3471d6c3b..f7023b5208 100644 --- a/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg +++ b/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg @@ -15,13 +15,13 @@ y_max = None # distance from ice margin to cull (km). # Set to a value <= 0 if you do not want # to cull based on distance from margin. -cull_distance = 10.0 +cull_distance = -100.0 # mesh density parameters # minimum cell spacing (meters) -min_spac = 3.e3 +min_spac = 4.e3 # maximum cell spacing (meters) -max_spac = 30.e3 +max_spac = 40.e3 # log10 of max speed for cell spacing high_log_speed = 2.5 # log10 of min speed for cell spacing diff --git a/compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg b/compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg new file mode 100644 index 0000000000..9abef17b26 --- /dev/null +++ b/compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg @@ -0,0 +1,74 @@ +# config options for high_res_mesh test case +[mesh] + +# number of levels in the mesh +levels = 10 + +# Bounds of GIS mesh. If any bound is set +# to None, the mesh will use the full extent +# of the gridded dataset. +x_min = None +x_max = None +y_min = None +y_max = None + +# distance from ice margin to cull (km). +# Set to a value <= 0 if you do not want +# to cull based on distance from margin. +cull_distance = -100.0 + +# number of processors to use for ESMF_RegridWeightGen +nProcs = 2048 + +# mesh density parameters +# minimum cell spacing (meters) +min_spac = 1.e3 +# maximum cell spacing (meters) +max_spac = 10.e3 +# log10 of max speed for cell spacing +high_log_speed = 2.5 +# log10 of min speed for cell spacing +low_log_speed = 0.75 +# distance at which cell spacing = max_spac +high_dist = 1.e5 +# distance within which cell spacing = min_spac +low_dist = 1.e4 +# distance at which cell spacing in ocean = max_spac +high_dist_coast = 10.e3 +# distance within which cell spaceing in ocean = min_spac +low_dist_coast = 2.e3 +# distance at which bed topography has no effect +high_dist_bed = 1.e5 +# distance within which bed topography has maximum effect +low_dist_bed = 7.5e4 +# Bed elev beneath which cell spacing is minimized +low_bed = 50.0 +# Bed elev above which cell spacing is maximized +high_bed = 100.0 + +# mesh density functions +use_speed = True +use_dist_to_grounding_line = False +use_dist_to_edge = True +use_dist_to_coast = True +use_bed = True + +[greenland] +# path to directory containing BedMachine and Measures datasets +# (default value is for Perlmutter) +data_path = /global/cfs/cdirs/fanssie/standard_datasets/GIS_datasets/ + +# filename of the BedMachine thickness and bedTopography dataset +# (default value is for Perlmutter) +bedmachine_filename = BedMachineGreenland-v6_edits_floodFill_extrap.nc + +# filename of the MEaSUREs ice velocity dataset +# (default value is for Perlmutter) +measures_filename = greenland_vel_mosaic500_extrap.nc + +# projection of the source datasets, according to the dictionary keys +# create_scrip_file_from_planar_rectangular_grid from MPAS_Tools +src_proj = gis-gimp + +# number of processors to use for ESMF_RegridWeightGen +nProcs = 2048 From b0170624f125ec8dcf4c880599cae79f43f3d803 Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Wed, 22 Apr 2026 20:31:42 -0700 Subject: [PATCH 05/12] Fix colorbar position in landice mask plots --- compass/landice/mesh.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compass/landice/mesh.py b/compass/landice/mesh.py index 6e3fdf51f7..197a78f8f3 100644 --- a/compass/landice/mesh.py +++ b/compass/landice/mesh.py @@ -557,11 +557,11 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name, fig, ax = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(9, 3)) margin_plot = ax[0].pcolor(margin_mask) gl_plot = ax[1].pcolor(grounding_line_mask) # noqa F841 - coast_plot = ax[2].pcolor(coast_mask) + coast_plot = ax[2].pcolor(coast_mask) # noqa F841 ax[0].set_title("margin mask") ax[1].set_title("grounding line mask") ax[2].set_title("coast mask") - plt.colorbar(margin_plot, ax=[ax[0], ax[1]], shrink=0.7) + plt.colorbar(margin_plot, ax=ax, shrink=0.7) [ax.set_aspect('equal') for ax in ax] fig.savefig("masks.png", dpi=400) From ea554693f0a64deb706e7a3b73e6e515d46c8c83 Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Wed, 22 Apr 2026 20:56:38 -0700 Subject: [PATCH 06/12] Fix unconditional reads from config file in set_cell_width Fix unconditional reads from config file in set_cell_width, so that the config file does not need to have high_dist_coast if use_dist_to_coast == False, for example. --- compass/landice/mesh.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/compass/landice/mesh.py b/compass/landice/mesh.py index 197a78f8f3..8033a844f3 100644 --- a/compass/landice/mesh.py +++ b/compass/landice/mesh.py @@ -298,17 +298,6 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, # Get config inputs for cell spacing functions min_spac = section.getfloat('min_spac') max_spac = section.getfloat('max_spac') - high_log_speed = section.getfloat('high_log_speed') - low_log_speed = section.getfloat('low_log_speed') - high_dist = section.getfloat('high_dist') - low_dist = section.getfloat('low_dist') - high_dist_coast = section.getfloat('high_dist_coast') - low_dist_coast = section.getfloat('low_dist_coast') - high_dist_bed = section.getfloat('high_dist_bed') - low_dist_bed = section.getfloat('low_dist_bed') - low_bed = section.getfloat('low_bed') - high_bed = section.getfloat('high_bed') - # convert km to m cull_distance = section.getfloat('cull_distance') * 1.e3 @@ -318,6 +307,10 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, # Cell spacing function based on union of masks if section.get('use_bed') == 'True': logger.info('Using bed elevation for spacing.') + high_dist_bed = section.getfloat('high_dist_bed') + low_dist_bed = section.getfloat('low_dist_bed') + low_bed = section.getfloat('low_bed') + high_bed = section.getfloat('high_bed') if flood_fill_iStart is not None and flood_fill_jStart is not None: logger.info('calling gridded_flood_fill to find ' + 'bedTopography <= low_bed connected to the ocean.') @@ -367,6 +360,8 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, # Make cell spacing function mapping from log speed to cell spacing if section.get('use_speed') == 'True': logger.info('Using speed for cell spacing') + high_log_speed = section.getfloat('high_log_speed') + low_log_speed = section.getfloat('low_log_speed') speed = (vx ** 2 + vy ** 2) ** 0.5 lspd = np.log10(speed) spacing_speed = np.interp(lspd, [low_log_speed, high_log_speed], @@ -390,6 +385,8 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, # Make cell spacing function mapping from distance to ice edge if section.get('use_dist_to_edge') == 'True': logger.info('Using distance to ice edge for cell spacing') + high_dist = section.getfloat('high_dist') + low_dist = section.getfloat('low_dist') spacing_edge = np.interp(dist_to_edge, [low_dist, high_dist], [min_spac, max_spac], left=min_spac, right=max_spac) @@ -399,6 +396,8 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, # Make cell spacing function mapping from distance to grounding line if section.get('use_dist_to_grounding_line') == 'True': logger.info('Using distance to grounding line for cell spacing') + high_dist = section.getfloat('high_dist') + low_dist = section.getfloat('low_dist') spacing_gl = np.interp(dist_to_grounding_line, [low_dist, high_dist], [min_spac, max_spac], left=min_spac, right=max_spac) @@ -408,6 +407,8 @@ def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, # Make cell spacing function mapping from distance to coast if section.get('use_dist_to_coast') == 'True': logger.info('Using distance to coast for cell spacing') + high_dist_coast = section.getfloat('high_dist_coast') + low_dist_coast = section.getfloat('low_dist_coast') spacing_coast = np.interp(dist_to_coast, [low_dist_coast, high_dist_coast], [min_spac, max_spac], left=min_spac, From 63adea4663af6fdd702fc582e8f2266d58d4f95a Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Wed, 22 Apr 2026 21:00:57 -0700 Subject: [PATCH 07/12] Update documentation for new Greenland mesh capabilities --- docs/developers_guide/landice/framework.rst | 18 ++++++++++----- .../landice/test_groups/greenland.rst | 6 ++++- .../landice/test_groups/greenland.rst | 23 ++++++++++++------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/docs/developers_guide/landice/framework.rst b/docs/developers_guide/landice/framework.rst index 9749cf7200..5422d71262 100644 --- a/docs/developers_guide/landice/framework.rst +++ b/docs/developers_guide/landice/framework.rst @@ -76,13 +76,17 @@ and edge coordinates to pass to py:func:`mpas_tools.mesh.creation.build_mesh.bui :py:func:`compass.landice.mesh.set_cell_width()` sets cell widths based on settings in config file to pass to :py:func:`mpas_tools.mesh.creation.build_mesh.build_planar_mesh()`. -Requires the following options to be set in the given config section: ``min_spac``, -``max_spac``, ``high_log_speed``, ``low_log_speed``, ``high_dist``, ``low_dist``, -``high_dist_bed``, ``low_dist_bed``, ``high_bed``, ``low_bed``, ``cull_distance``, -``use_speed``, ``use_dist_to_edge``, ``use_dist_to_grounding_line``, and ``use_bed``. +Always requires ``min_spac``, ``max_spac``, ``cull_distance``, ``use_speed``, +``use_dist_to_edge``, ``use_dist_to_grounding_line``, ``use_dist_to_coast``, and +``use_bed``. Additional options are only required when the corresponding density +function is enabled: ``high_log_speed``, ``low_log_speed`` (``use_speed = True``); +``high_dist``, ``low_dist`` (``use_dist_to_edge`` or ``use_dist_to_grounding_line = True``); +``high_dist_bed``, ``low_dist_bed``, ``high_bed``, ``low_bed`` (``use_bed = True``); +``high_dist_coast``, ``low_dist_coast`` (``use_dist_to_coast = True``). :py:func:`compass.landice.mesh.get_dist_to_edge_and_gl()` calculates distance from -each point to ice edge and grounding line, to be used in mesh density functions in +each point to ice edge, grounding line, and coast, returning a three-element tuple +``(dist_to_edge, dist_to_grounding_line, dist_to_coast)``. Used by :py:func:`compass.landice.mesh.set_cell_width()`. In future development, this should be updated to use a faster package such as `scikit-fmm`. @@ -154,6 +158,8 @@ Humboldt mesh): use_speed = True use_dist_to_grounding_line = False use_dist_to_edge = True + # When use_dist_to_coast = True, also set high_dist_coast and low_dist_coast + use_dist_to_coast = False use_bed = True # Optional interpolation inputs (skip when set to None) @@ -183,4 +189,4 @@ For mesh prototyping, it is a good idea to use `interpolate_data = False`, as the interpolation step can be slow and require many more resources than the rest of the mesh generation process. For instance, a 1–10km Greenland mesh can be created on a single Perlmutter CPU node, but data interpolation may -require up to 16 nodes. \ No newline at end of file +require up to 16 nodes. diff --git a/docs/developers_guide/landice/test_groups/greenland.rst b/docs/developers_guide/landice/test_groups/greenland.rst index 8d42426902..9c5c021f04 100644 --- a/docs/developers_guide/landice/test_groups/greenland.rst +++ b/docs/developers_guide/landice/test_groups/greenland.rst @@ -101,7 +101,7 @@ replace this hybrid method. Once the mesh is created, scrip files and the associated weights files are created for the mesh and observational data sets. Then, ice geometry and -velocity observations are conservatively remapped from BedMachine v5 and +velocity observations are conservatively remapped from BedMachine v6 and MEaSUREs 2006-2010 data sets. Finally, there is some cleanup to set large velocity uncertainties outside the ice mask, check the sign of the basal heat flux, and set reasonable values for dH/dt and its uncertainty. @@ -111,3 +111,7 @@ The BedMachine and MEaSUREs remapping steps are optional and are controlled by is unset (empty or ``None``), that dataset interpolation is skipped. The default config includes both datasets, so interpolation is enabled by default. The base-mesh projection in ``build_mali_mesh()`` is fixed for this test case. + +An alternative config file, ``mesh_gen_1to10km.cfg``, creates a finer 1–10km +mesh using ``use_dist_to_coast = True`` to refine ocean resolution near the +coast and a negative ``cull_distance`` to retain the open ocean. diff --git a/docs/users_guide/landice/test_groups/greenland.rst b/docs/users_guide/landice/test_groups/greenland.rst index 03a0dcb875..f20f7dab1d 100644 --- a/docs/users_guide/landice/test_groups/greenland.rst +++ b/docs/users_guide/landice/test_groups/greenland.rst @@ -36,9 +36,6 @@ The other test cases do not use config options. # config options for high_res_mesh test case [mesh] - # path to directory containing BedMachine and Measures datasets - data_path = /global/cfs/cdirs/fanssie/standard_datasets/GIS_datasets/ - # number of levels in the mesh levels = 10 @@ -53,13 +50,13 @@ The other test cases do not use config options. # distance from ice margin to cull (km). # Set to a value <= 0 if you do not want # to cull based on distance from margin. - cull_distance = 10.0 + cull_distance = -100.0 # mesh density parameters # minimum cell spacing (meters) - min_spac = 3.e3 + min_spac = 4.e3 # maximum cell spacing (meters) - max_spac = 30.e3 + max_spac = 40.e3 # log10 of max speed for cell spacing high_log_speed = 2.5 # log10 of min speed for cell spacing @@ -68,6 +65,10 @@ The other test cases do not use config options. high_dist = 1.e5 # distance within which cell spacing = min_spac low_dist = 1.e4 + # distance at which cell spacing in ocean = max_spac + high_dist_coast = 16.e3 + # distance within which cell spacing in ocean = min_spac + low_dist_coast = 8.e3 # distance at which bed topography has no effect high_dist_bed = 1.e5 # distance within which bed topography has maximum effect @@ -81,6 +82,7 @@ The other test cases do not use config options. use_speed = True use_dist_to_grounding_line = False use_dist_to_edge = True + use_dist_to_coast = True use_bed = True [greenland] @@ -146,8 +148,8 @@ The test case performs interpolation of observational data from gridded datasets to the Greenland mesh. This takes care of the peculiarities of the current gridded compilation dataset (greenland_1km_2024_01_29.epsg3413.icesheetonly.nc), as well as using conservative remapping directly from the high-resolution -BedMachine v5 and MeASUReS 2006-2010 velocity datasets. There is a fairly -heavy degree of pre-processing done to get the BedMachine and MeASUReS +BedMachine v6 and MEaSUREs 2006-2010 velocity datasets. There is a fairly +heavy degree of pre-processing done to get the BedMachine and MEaSUREs datasets ready to be used here. The pre-processing includes renaming variables, setting reasonable _FillValue and missing_value attributes extrapolating fields to avoid interpolation ramps at ice margins, @@ -160,6 +162,11 @@ the processed data files could be added to the server on Anvil and downloaded as needed. However, until then, this test case provides a reproducible workflow for setting up Greenland meshes at varying resolutions. +An alternative config file, ``mesh_gen_1to10km.cfg``, is provided for +creating a finer 1–10km mesh. It uses ``use_dist_to_coast = True`` to +refine ocean resolution near the coast and sets ``cull_distance = -100`` +to retain the open ocean in the mesh domain. + The BedMachine and MEaSUREs interpolation is optional. If ``data_path`` or the corresponding filename in ``[greenland]`` is unset (empty or ``None``), that dataset interpolation step is skipped. The default config values include both From de843b53b9953d1e39de28562f70f8fac7abbbf3 Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Thu, 23 Apr 2026 13:48:42 -0700 Subject: [PATCH 08/12] Remove mention of using scikit-fmm Remove mention of using scikit-fmm to calculate distances to grounding line, coast, and margin, since this algorithm is now very fast. --- compass/landice/mesh.py | 3 +-- docs/developers_guide/landice/framework.rst | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compass/landice/mesh.py b/compass/landice/mesh.py index 8033a844f3..1bb2a296eb 100644 --- a/compass/landice/mesh.py +++ b/compass/landice/mesh.py @@ -447,8 +447,7 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name, """ Calculate distance from each point to ice edge and grounding line, to be used in mesh density functions in - :py:func:`compass.landice.mesh.set_cell_width()`. In future development, - this should be updated to use a faster package such as ``scikit-fmm``. + :py:func:`compass.landice.mesh.set_cell_width()`. Parameters ---------- diff --git a/docs/developers_guide/landice/framework.rst b/docs/developers_guide/landice/framework.rst index 5422d71262..e21eea21cc 100644 --- a/docs/developers_guide/landice/framework.rst +++ b/docs/developers_guide/landice/framework.rst @@ -87,8 +87,7 @@ function is enabled: ``high_log_speed``, ``low_log_speed`` (``use_speed = True`` :py:func:`compass.landice.mesh.get_dist_to_edge_and_gl()` calculates distance from each point to ice edge, grounding line, and coast, returning a three-element tuple ``(dist_to_edge, dist_to_grounding_line, dist_to_coast)``. Used by -:py:func:`compass.landice.mesh.set_cell_width()`. In future development, -this should be updated to use a faster package such as `scikit-fmm`. +:py:func:`compass.landice.mesh.set_cell_width()`. :py:func:`compass.landice.mesh.build_cell_width()` determine final MPAS mesh cell sizes using desired cell widths calculated by py:func:`compass.landice.mesh.set_cell_width()`, From 15f86b6c00ad1b8acd29b38919c971229104f4e5 Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Mon, 27 Apr 2026 14:32:45 -0600 Subject: [PATCH 09/12] Make coast mask definition consistent with description Define coast mask as last land or ice cell adjacent to ocean, rather than last ocean cell adjacent to land or ice. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- compass/landice/mesh.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compass/landice/mesh.py b/compass/landice/mesh.py index 1bb2a296eb..601a5fefbf 100644 --- a/compass/landice/mesh.py +++ b/compass/landice/mesh.py @@ -545,14 +545,15 @@ def get_dist_to_edge_and_gl(self, thk, topg, x, y, section_name, grounding_line_mask = np.logical_or(grounding_line_mask, not_grounded_mask) - not_ocean_mask = np.logical_not(np.roll(ocean_mask, n, axis=[0, 1])) - coast_mask = np.logical_or(coast_mask, not_ocean_mask) + shifted_ocean = np.roll(ocean_mask, n, axis=[0, 1]) + coast_mask = np.logical_or(coast_mask, shifted_ocean) # where ice exists and neighbors non-ice locations margin_mask = np.logical_and(margin_mask, ice_mask) # where grounded ice exists and neighbors floating ice grounding_line_mask = np.logical_and(grounding_line_mask, grounded_mask) - coast_mask = np.logical_and(coast_mask, ocean_mask) + # where non-ocean exists and neighbors ocean locations + coast_mask = np.logical_and(coast_mask, np.logical_not(ocean_mask)) fig, ax = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(9, 3)) margin_plot = ax[0].pcolor(margin_mask) From 886424e9284b75d6937908dca8542c2f438e079d Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Mon, 27 Apr 2026 14:33:42 -0600 Subject: [PATCH 10/12] Apply suggestions from code review Fix minor typos. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg b/compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg index 9abef17b26..2182788aed 100644 --- a/compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg +++ b/compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg @@ -17,9 +17,6 @@ y_max = None # to cull based on distance from margin. cull_distance = -100.0 -# number of processors to use for ESMF_RegridWeightGen -nProcs = 2048 - # mesh density parameters # minimum cell spacing (meters) min_spac = 1.e3 @@ -35,7 +32,7 @@ high_dist = 1.e5 low_dist = 1.e4 # distance at which cell spacing in ocean = max_spac high_dist_coast = 10.e3 -# distance within which cell spaceing in ocean = min_spac +# distance within which cell spacing in ocean = min_spac low_dist_coast = 2.e3 # distance at which bed topography has no effect high_dist_bed = 1.e5 From 8c92f5386a602df9a461c0a98abc49837c498cbb Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Mon, 27 Apr 2026 13:38:51 -0700 Subject: [PATCH 11/12] Make `bed` required input argument for set_cell_width() Make `bed` required input argument for set_cell_width(), in keeping with new land and ocean masks that use `bed` for calculations. Also some minor cleanup from code review: fix typo and set `interpolate_data` in greenland 1-10km .cfg file. --- compass/landice/mesh.py | 2 +- compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg | 2 +- compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compass/landice/mesh.py b/compass/landice/mesh.py index 601a5fefbf..0d7455bcc4 100644 --- a/compass/landice/mesh.py +++ b/compass/landice/mesh.py @@ -224,7 +224,7 @@ def clip_mesh_to_bounding_box(mask_ds, base_ds, bounding_box): return mask_ds -def set_cell_width(self, section_name, thk, bed=None, vx=None, vy=None, +def set_cell_width(self, section_name, thk, bed, vx=None, vy=None, dist_to_edge=None, dist_to_grounding_line=None, dist_to_coast=None, flood_fill_iStart=None, flood_fill_jStart=None): diff --git a/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg b/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg index f7023b5208..5fa05c99a5 100644 --- a/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg +++ b/compass/landice/tests/greenland/mesh_gen/mesh_gen.cfg @@ -32,7 +32,7 @@ high_dist = 1.e5 low_dist = 1.e4 # distance at which cell spacing in ocean = max_spac high_dist_coast = 16.e3 -# distance within which cell spaceing in ocean = min_spac +# distance within which cell spacing in ocean = min_spac low_dist_coast = 8.e3 # distance at which bed topography has no effect high_dist_bed = 1.e5 diff --git a/compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg b/compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg index 2182788aed..0c06f6e393 100644 --- a/compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg +++ b/compass/landice/tests/greenland/mesh_gen/mesh_gen_1to10km.cfg @@ -51,6 +51,8 @@ use_dist_to_coast = True use_bed = True [greenland] +# Whether to interpolate data (controls run_optional_interpolation) +interpolate_data = True # path to directory containing BedMachine and Measures datasets # (default value is for Perlmutter) data_path = /global/cfs/cdirs/fanssie/standard_datasets/GIS_datasets/ From 2eefef65cbb84f8ec405ca60569f145a2134da1e Mon Sep 17 00:00:00 2001 From: Trevor Hillebrand Date: Mon, 27 Apr 2026 13:47:43 -0700 Subject: [PATCH 12/12] Update docs to reflect which settings are now optional. Update framework in users and developers guides to explain which .cfg settings are required and which are optional. --- docs/developers_guide/landice/framework.rst | 55 ++++++++++++++++----- docs/users_guide/landice/framework.rst | 51 +++++++++++++++---- 2 files changed, 84 insertions(+), 22 deletions(-) diff --git a/docs/developers_guide/landice/framework.rst b/docs/developers_guide/landice/framework.rst index e21eea21cc..463187675a 100644 --- a/docs/developers_guide/landice/framework.rst +++ b/docs/developers_guide/landice/framework.rst @@ -108,12 +108,30 @@ applied to the mask, not to the rectangular geometry passed to defined in Geometric Features repository. It is only used by the ``antarctica`` and ``greenland`` test cases. -The following config options should be defined for all ``mesh_gen`` test cases (although -not necessarily with the same values shown here, which are the defaults for the 1–10km -Humboldt mesh): +The config options below should be defined for all ``mesh_gen`` test cases +(although not necessarily with the same values shown here, which are the +defaults for the 1–10km Humboldt mesh). + +A core set of options is **always required**: ``levels``, +``x_min``/``x_max``/``y_min``/``y_max``, ``cull_distance``, ``min_spac``, +``max_spac``, and the five density-function toggles ``use_speed``, +``use_dist_to_edge``, ``use_dist_to_grounding_line``, ``use_dist_to_coast``, +and ``use_bed``. + +The remaining mesh-density parameters are **only required when their +corresponding toggle is enabled**, so configs that disable a density function +may omit the associated options entirely: + +* ``use_speed = True`` requires ``high_log_speed`` and ``low_log_speed``. +* ``use_dist_to_edge = True`` or ``use_dist_to_grounding_line = True`` + requires ``high_dist`` and ``low_dist``. +* ``use_dist_to_coast = True`` requires ``high_dist_coast`` and + ``low_dist_coast``. +* ``use_bed = True`` requires ``high_dist_bed``, ``low_dist_bed``, + ``high_bed``, and ``low_bed``. .. code-block:: cfg - + # config options for humboldt test cases [mesh] @@ -136,14 +154,35 @@ Humboldt mesh): min_spac = 1.e3 # maximum cell spacing (meters) max_spac = 1.e4 + + # mesh density functions + use_speed = True + use_dist_to_grounding_line = False + use_dist_to_edge = True + # When use_dist_to_coast = True, also set high_dist_coast and low_dist_coast + use_dist_to_coast = False + use_bed = True + + # Required when use_speed = True # log10 of max speed (m/yr) for cell spacing high_log_speed = 2.5 # log10 of min speed (m/yr) for cell spacing low_log_speed = 0.75 + + # Required when use_dist_to_edge = True or + # use_dist_to_grounding_line = True # distance at which cell spacing = max_spac (meters) high_dist = 1.e5 # distance within which cell spacing = min_spac (meters) low_dist = 1.e4 + + # Required when use_dist_to_coast = True + # distance at which cell spacing in ocean = max_spac (meters) + high_dist_coast = 16.e3 + # distance within which cell spacing in ocean = min_spac (meters) + low_dist_coast = 8.e3 + + # Required when use_bed = True # distance at which bed topography has no effect high_dist_bed = 1.e5 # distance within which bed topography has maximum effect @@ -153,14 +192,6 @@ Humboldt mesh): # Bed elev above which cell spacing is maximized high_bed = 100.0 - # mesh density functions - use_speed = True - use_dist_to_grounding_line = False - use_dist_to_edge = True - # When use_dist_to_coast = True, also set high_dist_coast and low_dist_coast - use_dist_to_coast = False - use_bed = True - # Optional interpolation inputs (skip when set to None) # Whether to interpolate data (controls run_optional_interpolation) interpolate_data = False diff --git a/docs/users_guide/landice/framework.rst b/docs/users_guide/landice/framework.rst index 89222b9a51..d6ab38b0cf 100644 --- a/docs/users_guide/landice/framework.rst +++ b/docs/users_guide/landice/framework.rst @@ -14,9 +14,27 @@ from a similar script in MPAS-Tools. mesh ~~~~ -The following config options should be defined for all ``mesh_gen`` test cases (although -not necessarily with the same values shown here, which are the defaults for the 1–10km -Humboldt mesh): +The config options below should be defined for all ``mesh_gen`` test cases +(although not necessarily with the same values shown here, which are the +defaults for the 1–10km Humboldt mesh). + +A core set of options is **always required**: ``levels``, +``x_min``/``x_max``/``y_min``/``y_max``, ``cull_distance``, ``min_spac``, +``max_spac``, and the five density-function toggles ``use_speed``, +``use_dist_to_edge``, ``use_dist_to_grounding_line``, ``use_dist_to_coast``, +and ``use_bed``. + +The remaining mesh-density parameters are **only required when their +corresponding toggle is enabled**, so configs that disable a density function +may omit the associated options entirely: + +* ``use_speed = True`` requires ``high_log_speed`` and ``low_log_speed``. +* ``use_dist_to_edge = True`` or ``use_dist_to_grounding_line = True`` + requires ``high_dist`` and ``low_dist``. +* ``use_dist_to_coast = True`` requires ``high_dist_coast`` and + ``low_dist_coast``. +* ``use_bed = True`` requires ``high_dist_bed``, ``low_dist_bed``, + ``high_bed``, and ``low_bed``. .. code-block:: cfg @@ -44,16 +62,35 @@ Humboldt mesh): min_spac = 1.e3 # maximum cell spacing (meters) max_spac = 1.e4 + + # mesh density functions + use_speed = True + use_dist_to_grounding_line = False + use_dist_to_edge = True + # When use_dist_to_coast = True, also set high_dist_coast and low_dist_coast + use_dist_to_coast = False + use_bed = True + + # Required when use_speed = True # log10 of max speed (m/yr) for cell spacing high_log_speed = 2.5 # log10 of min speed (m/yr) for cell spacing low_log_speed = 0.75 + + # Required when use_dist_to_edge = True or + # use_dist_to_grounding_line = True # distance at which cell spacing = max_spac (meters) high_dist = 1.e5 # distance within which cell spacing = min_spac (meters) low_dist = 1.e4 - # These *_bed settings are only applied when use_bed = True. + # Required when use_dist_to_coast = True + # distance at which cell spacing in ocean = max_spac (meters) + high_dist_coast = 16.e3 + # distance within which cell spacing in ocean = min_spac (meters) + low_dist_coast = 8.e3 + + # Required when use_bed = True # distance at which bed topography has no effect high_dist_bed = 1.e5 # distance within which bed topography has maximum effect @@ -62,9 +99,3 @@ Humboldt mesh): low_bed = 50.0 # Bed elev above which cell spacing is maximized high_bed = 100.0 - - # mesh density functions - use_speed = True - use_dist_to_grounding_line = False - use_dist_to_edge = True - use_bed = True