PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
FrontRetreat.cc
Go to the documentation of this file.
1 /* Copyright (C) 2016, 2017, 2018, 2019, 2020, 2022, 2023 PISM Authors
2  *
3  * This file is part of PISM.
4  *
5  * PISM is free software; you can redistribute it and/or modify it under the
6  * terms of the GNU General Public License as published by the Free Software
7  * Foundation; either version 3 of the License, or (at your option) any later
8  * version.
9  *
10  * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with PISM; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 #include "pism/frontretreat/FrontRetreat.hh"
20 
21 #include "pism/util/array/CellType.hh"
22 #include "pism/util/MaxTimestep.hh"
23 #include "pism/util/pism_utilities.hh"
24 #include "pism/geometry/part_grid_threshold_thickness.hh"
25 #include "pism/geometry/Geometry.hh"
26 #include "pism/util/Context.hh"
27 
28 namespace pism {
29 
30 FrontRetreat::FrontRetreat(std::shared_ptr<const Grid> g)
31  : Component(g),
32  m_cell_type(m_grid, "cell_type"),
33  m_tmp(m_grid, "temporary_storage") {
34 
35  m_tmp.metadata(0).long_name("additional mass loss at points near the front").units("m");
36  m_cell_type.metadata(0).long_name("cell type mask");
37 }
38 
39 /*!
40  * Compute the modified mask to avoid "wrapping around" of front retreat at domain
41  * boundaries.
42  */
44  array::CellType1 &output) const {
45 
46  array::AccessScope list{&input, &output};
47 
48  const int Mx = m_grid->Mx();
49  const int My = m_grid->My();
50 
51  ParallelSection loop(m_grid->com);
52  try {
53  for (auto p = m_grid->points(1); p; p.next()) {
54  const int i = p.i(), j = p.j();
55 
56  if (i < 0 or i >= Mx or j < 0 or j >= My) {
57  output(i, j) = MASK_ICE_FREE_OCEAN;
58  } else {
59  output(i, j) = input(i, j);
60  }
61  }
62  } catch (...) {
63  loop.failed();
64  }
65  loop.check();
66 }
67 
68 /*!
69  * Compute the maximum time step length provided a horizontal retreat rate.
70  */
72  const array::Scalar &bc_mask,
73  const array::Scalar &retreat_rate) const {
74 
75  auto grid = retreat_rate.grid();
76  auto sys = grid->ctx()->unit_system();
77 
78  using units::convert;
79 
80  // About 9 hours which corresponds to 10000 km year-1 on a 10 km grid
81  double dt_min = convert(sys, 0.001, "years", "seconds");
82 
83  double
84  retreat_rate_max = 0.0,
85  retreat_rate_mean = 0.0;
86  int N_cells = 0;
87 
88  array::AccessScope list{&cell_type, &bc_mask, &retreat_rate};
89 
90  for (auto pt = grid->points(); pt; pt.next()) {
91  const int i = pt.i(), j = pt.j();
92 
93  if (cell_type.ice_free_ocean(i, j) and
94  cell_type.next_to_ice(i, j) and
95  bc_mask(i, j) < 0.5) {
96  // NB: this condition has to match the one in update_geometry()
97 
98  const double C = retreat_rate(i, j);
99 
100  N_cells += 1;
101  retreat_rate_mean += C;
102  retreat_rate_max = std::max(C, retreat_rate_max);
103  }
104  }
105 
106  N_cells = GlobalSum(grid->com, N_cells);
107  retreat_rate_mean = GlobalSum(grid->com, retreat_rate_mean);
108  retreat_rate_max = GlobalMax(grid->com, retreat_rate_max);
109 
110  if (N_cells > 0.0) {
111  retreat_rate_mean /= N_cells;
112  } else {
113  retreat_rate_mean = 0.0;
114  }
115 
116  double denom = retreat_rate_max / grid->dx();
117  const double epsilon = convert(sys, 0.001 / (grid->dx() + grid->dy()), "seconds", "years");
118 
119  double dt = 1.0 / (denom + epsilon);
120 
121  m_log->message(3,
122  " frontal retreat: maximum rate = %.2f m/year gives dt=%.5f years\n"
123  " mean rate = %.2f m/year over %d cells\n",
124  convert(m_sys, retreat_rate_max, "m second-1", "m year-1"),
125  convert(m_sys, dt, "seconds", "years"),
126  convert(m_sys, retreat_rate_mean, "m second-1", "m year-1"),
127  N_cells);
128 
129  return MaxTimestep(std::max(dt, dt_min), "front_retreat");
130 }
131 
132 /*!
133  * Update ice geometry by applying a horizontal retreat rate.
134  *
135  * This code applies a horizontal retreat rate at "partially-filled" cells that are next
136  * to icy cells.
137  *
138  * Models providing the "retreat rate" should set this field to zero in areas where a
139  * particular parameterization does not apply. (For example: some calving models apply at
140  * shelf calving fronts, others may apply at grounded termini but not at ice shelves,
141  * etc).
142  */
144  const Geometry &geometry,
145  const array::Scalar1 &bc_mask,
146  const array::Scalar &retreat_rate,
147  array::Scalar &Href,
148  array::Scalar1 &ice_thickness) {
149 
150  const array::Scalar1 &bed = geometry.bed_elevation;
151  const array::Scalar1 &sea_level = geometry.sea_level_elevation;
152  const array::Scalar1 &surface_elevation = geometry.ice_surface_elevation;
153 
154  if (m_config->get_flag("geometry.front_retreat.wrap_around")) {
155  m_cell_type.copy_from(geometry.cell_type);
156  } else {
157  // compute the modified mask needed to prevent front retreat from "wrapping around"
158  // the computational domain
160  }
161 
162  const double dx = m_grid->dx();
163 
164  m_tmp.set(0.0);
165 
166  array::AccessScope list{&ice_thickness, &bc_mask,
167  &bed, &sea_level, &m_cell_type, &Href, &m_tmp, &retreat_rate,
168  &surface_elevation};
169 
170  // Step 1: Apply the computed horizontal retreat rate:
171  for (auto pt = m_grid->points(); pt; pt.next()) {
172  const int i = pt.i(), j = pt.j();
173 
174  // apply retreat rate at the margin (i.e. to partially-filled cells) only
175  if (m_cell_type.ice_free_ocean(i, j) and
176  m_cell_type.next_to_ice(i, j) and
177  bc_mask(i, j) < 0.5) {
178  // NB: this condition has to match the one in max_timestep()
179 
180  const double
181  rate = retreat_rate(i, j),
182  Href_old = Href(i, j);
183 
184  // Compute the number of floating neighbors and the neighbor-averaged ice thickness:
185  double H_threshold = part_grid_threshold_thickness(m_cell_type.star_int(i, j),
186  ice_thickness.star(i, j),
187  surface_elevation.star(i, j),
188  bed(i, j));
189 
190  // Calculate mass loss with respect to the associated ice thickness and the grid size:
191  const double Href_change = -dt * rate * H_threshold / dx; // in m
192 
193  if (Href_old + Href_change >= 0.0) {
194  // Href is high enough to absorb the mass loss
195  Href(i, j) = Href_old + Href_change;
196  } else {
197  Href(i, j) = 0.0;
198  // Href + Href_change is negative: need to distribute mass loss to neighboring points
199 
200  // Find the number of neighbors to distribute to.
201  //
202  // We consider floating cells and grounded cells with the base below sea level. In other
203  // words, additional mass losses are distributed to shelf calving fronts and grounded marine
204  // termini.
205  int N = 0;
206  {
207  auto M = m_cell_type.star(i, j);
208  auto BC = bc_mask.star_int(i, j);
209 
210  auto
211  b = bed.star(i, j),
212  sl = sea_level.star(i, j);
213 
214  for (auto d : {North, East, South, West}) {
215  int m = M[d];
216  int bc = BC[d];
217 
218  // note: this is where the modified cell type mask is used to prevent
219  // wrap-around
220  if (bc == 0 and // distribute to regular (*not* Dirichlet B.C.) neighbors only
221  (mask::floating_ice(m) or
222  (mask::grounded_ice(m) and b[d] < sl[d]))) {
223  N += 1;
224  }
225  }
226  }
227 
228  if (N > 0) {
229  m_tmp(i, j) = (Href_old + Href_change) / (double)N;
230  } else {
231  // No shelf calving front of grounded terminus to distribute to: retreat stops here.
232  m_tmp(i, j) = 0.0;
233  }
234  }
235 
236  } // end of "if ice free ocean next to ice and not a BC location "
237  } // end of loop over grid points
238 
239  // Step 2: update ice thickness and Href in neighboring cells if we need to propagate mass losses.
241 
242  for (auto p = m_grid->points(); p; p.next()) {
243  const int i = p.i(), j = p.j();
244 
245  // Note: this condition has to match the one in step 1 above.
246  if (bc_mask.as_int(i, j) == 0 and
247  (m_cell_type.floating_ice(i, j) or
248  (m_cell_type.grounded_ice(i, j) and bed(i, j) < sea_level(i, j)))) {
249 
250  const double delta_H = (m_tmp(i + 1, j) + m_tmp(i - 1, j) +
251  m_tmp(i, j + 1) + m_tmp(i, j - 1));
252 
253  if (delta_H < 0.0) {
254  Href(i, j) = ice_thickness(i, j) + delta_H; // in m
255  ice_thickness(i, j) = 0.0;
256  }
257 
258  // Stop retreat if the current cell does not have enough ice to absorb the loss.
259  if (Href(i, j) < 0.0) {
260  Href(i, j) = 0.0;
261  }
262  }
263  }
264 }
265 
266 } // end of namespace pism
const units::System::Ptr m_sys
unit system used by this component
Definition: Component.hh:160
std::shared_ptr< const Grid > grid() const
Definition: Component.cc:105
const Config::ConstPtr m_config
configuration database used by this component
Definition: Component.hh:158
const Logger::ConstPtr m_log
logger (for easy access)
Definition: Component.hh:162
const std::shared_ptr< const Grid > m_grid
grid used by this component
Definition: Component.hh:156
A class defining a common interface for most PISM sub-models.
Definition: Component.hh:118
void update_geometry(double dt, const Geometry &geometry, const array::Scalar1 &bc_mask, const array::Scalar &retreat_rate, array::Scalar &Href, array::Scalar1 &ice_thickness)
array::CellType1 m_cell_type
Definition: FrontRetreat.hh:60
MaxTimestep max_timestep(const array::CellType1 &cell_type, const array::Scalar &bc_mask, const array::Scalar &retreat_rate) const
Definition: FrontRetreat.cc:71
array::Scalar1 m_tmp
Definition: FrontRetreat.hh:63
FrontRetreat(std::shared_ptr< const Grid > g)
Definition: FrontRetreat.cc:30
void compute_modified_mask(const array::CellType1 &input, array::CellType1 &output) const
Definition: FrontRetreat.cc:43
array::Scalar1 sea_level_elevation
Definition: Geometry.hh:48
array::Scalar2 ice_surface_elevation
Definition: Geometry.hh:57
array::CellType2 cell_type
Definition: Geometry.hh:55
array::Scalar2 bed_elevation
Definition: Geometry.hh:47
Combines the max. time step with the flag indicating if a restriction is active. Makes is possible to...
Definition: MaxTimestep.hh:31
void failed()
Indicates a failure of a parallel section.
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition: Array.hh:65
void copy_from(const Array2D< T > &source)
Definition: Array2D.hh:73
stencils::Star< T > star(int i, int j) const
Definition: Array2D.hh:79
std::shared_ptr< const Grid > grid() const
Definition: Array.cc:132
void set(double c)
Result: v[j] <- c for all j.
Definition: Array.cc:707
void update_ghosts()
Updates ghost points.
Definition: Array.cc:693
SpatialVariableMetadata & metadata(unsigned int N=0)
Returns a reference to the SpatialVariableMetadata object containing metadata for the compoment N.
Definition: Array.cc:553
bool next_to_ice(int i, int j) const
Ice-free margin (at least one of four neighbors has ice).
Definition: CellType.hh:87
stencils::Star< int > star_int(int i, int j) const
Definition: Scalar.hh:72
bool floating_ice(int i, int j) const
Definition: CellType.hh:50
bool ice_free_ocean(int i, int j) const
Definition: CellType.hh:58
bool grounded_ice(int i, int j) const
Definition: CellType.hh:46
stencils::Star< int > star_int(int i, int j) const
Definition: Scalar.hh:72
int as_int(int i, int j) const
Definition: Scalar.hh:45
double max(const array::Scalar &input)
Finds maximum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:165
bool grounded_ice(int M)
Definition: Mask.hh:51
bool floating_ice(int M)
Definition: Mask.hh:54
double convert(System::Ptr system, double input, const std::string &spec1, const std::string &spec2)
Convert a quantity from unit1 to unit2.
Definition: Units.cc:70
static const double g
Definition: exactTestP.cc:36
void GlobalMax(MPI_Comm comm, double *local, double *result, int count)
double part_grid_threshold_thickness(stencils::Star< int > cell_type, stencils::Star< double > ice_thickness, stencils::Star< double > surface_elevation, double bed_elevation)
Compute threshold thickness used when deciding if a partially-filled cell should be considered 'full'...
@ MASK_ICE_FREE_OCEAN
Definition: Mask.hh:35
@ North
Definition: stencils.hh:24
@ East
Definition: stencils.hh:24
@ South
Definition: stencils.hh:24
@ West
Definition: stencils.hh:24
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)
const int C[]
Definition: ssafd_code.cc:44