PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
SSA.cc
Go to the documentation of this file.
1 // Copyright (C) 2004--2019, 2021, 2022, 2023 Constantine Khroulev, Ed Bueler, Jed Brown, Torsten Albrecht
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/stressbalance/ssa/SSA.hh"
20 #include "pism/util/EnthalpyConverter.hh"
21 #include "pism/rheology/FlowLawFactory.hh"
22 #include "pism/util/Mask.hh"
23 #include "pism/util/error_handling.hh"
24 #include "pism/util/io/File.hh"
25 #include "pism/util/array/CellType.hh"
26 #include "pism/stressbalance/StressBalance.hh"
27 #include "pism/geometry/Geometry.hh"
28 
29 #include "pism/stressbalance/ssa/SSA_diagnostics.hh"
30 
31 namespace pism {
32 namespace stressbalance {
33 
35  m_min_thickness = config.get_number("stress_balance.ssa.strength_extension.min_thickness");
36  m_constant_nu = config.get_number("stress_balance.ssa.strength_extension.constant_nu");
37 }
38 
39 //! Set strength = (viscosity times thickness).
40 /*! Determines nu by input strength and current min_thickness. */
42  if (my_nuH <= 0.0) {
43  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "nuH must be positive, got %f", my_nuH);
44  }
45  m_constant_nu = my_nuH / m_min_thickness;
46 }
47 
48 //! Set minimum thickness to trigger use of extension.
49 /*! Preserves strength (nuH) by also updating using current nu. */
50 void SSAStrengthExtension::set_min_thickness(double my_min_thickness) {
51  if (my_min_thickness <= 0.0) {
52  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "min_thickness must be positive, got %f",
53  my_min_thickness);
54  }
55  double nuH = m_constant_nu * m_min_thickness;
56  m_min_thickness = my_min_thickness;
58 }
59 
60 //! Returns strength = (viscosity times thickness).
63 }
64 
65 //! Returns minimum thickness to trigger use of extension.
67  return m_min_thickness;
68 }
69 
70 
71 SSA::SSA(std::shared_ptr<const Grid> g)
73  m_mask(m_grid, "ssa_mask"),
74  m_taud(m_grid, "taud"),
75  m_velocity_global(m_grid, "bar")
76 {
77 
78  m_e_factor = m_config->get_number("stress_balance.ssa.enhancement_factor");
79 
81 
82  // grounded_dragging_floating integer mask
83  m_mask.metadata(0)
84  .long_name("ice-type (ice-free/grounded/floating/ocean) integer mask");
87  m_mask.metadata()["flag_meanings"] = "ice_free_bedrock grounded_ice floating_ice ice_free_ocean";
88 
89  m_taud.metadata(0)
90  .long_name("X-component of the driving shear stress at the base of ice")
91  .units("Pa");
92  m_taud.metadata(1)
93  .long_name("Y-component of the driving shear stress at the base of ice")
94  .units("Pa");
95 
96  // override velocity metadata
97  m_velocity.metadata(0).set_name("u_ssa");
98  m_velocity.metadata(0).long_name("SSA model ice velocity in the X direction");
99 
100  m_velocity.metadata(1).set_name("v_ssa");
101  m_velocity.metadata(1).long_name("SSA model ice velocity in the Y direction");
102 
104 
105  {
106  rheology::FlowLawFactory ice_factory("stress_balance.ssa.", m_config, m_EC);
107  ice_factory.remove(ICE_GOLDSBY_KOHLSTEDT);
108  m_flow_law = ice_factory.create();
109  }
110 }
111 
113  if (strength_extension != NULL) {
114  delete strength_extension;
115  strength_extension = NULL;
116  }
117 }
118 
119 
120 //! \brief Initialize a generic regular-grid SSA solver.
122 
124 
125  m_log->message(2, "* Initializing the SSA stress balance...\n");
126  m_log->message(2,
127  " [using the %s flow law]\n", m_flow_law->name().c_str());
128 
130 
131  // Check if PISM is being initialized from an output file from a previous run
132  // and read the initial guess (unless asked not to).
133  if (opts.type == INIT_RESTART) {
134  if (m_config->get_flag("stress_balance.ssa.read_initial_guess")) {
135  File input_file(m_grid->com, opts.filename, io::PISM_GUESS, io::PISM_READONLY);
136  bool u_ssa_found = input_file.find_variable("u_ssa");
137  bool v_ssa_found = input_file.find_variable("v_ssa");
138  unsigned int start = input_file.nrecords() - 1;
139 
140  if (u_ssa_found and v_ssa_found) {
141  m_log->message(3, "Reading u_ssa and v_ssa...\n");
142 
143  m_velocity.read(input_file, start);
144  }
145  }
146  } else {
147  m_velocity.set(0.0); // default initial guess
148  }
149 }
150 
151 //! \brief Update the SSA solution.
152 void SSA::update(const Inputs &inputs, bool full_update) {
153 
154  // update the cell type mask using the ice-free thickness threshold for stress balance
155  // computations
156  {
157  const double H_threshold = m_config->get_number("stress_balance.ice_free_thickness_standard");
159  gc.set_icefree_thickness(H_threshold);
160 
162  inputs.geometry->bed_elevation,
163  inputs.geometry->ice_thickness,
164  m_mask);
165  }
166 
167  if (full_update) {
168  solve(inputs);
170  *inputs.basal_yield_stress,
171  m_mask,
173  }
174 }
175 
176 /*!
177  * Compute the weight used to determine if the difference between locations `i,j` and `n`
178  * (neighbor) should be used in the computation of the surface gradient in
179  * SSA::compute_driving_stress().
180  *
181  * We avoid differencing across
182  *
183  * - ice margins if stress boundary condition at ice margins (CFBC) is active
184  * - grounding lines
185  * - ice margins next to ice free locations above the surface elevation of the ice (fjord
186  * walls, nunataks, headwalls)
187  */
188 static int weight(bool margin_bc,
189  int M_ij, int M_n,
190  double h_ij, double h_n,
191  int N_ij, int N_n) {
192  using mask::grounded;
193  using mask::icy;
194  using mask::floating_ice;
195  using mask::ice_free;
196  using mask::ice_free_ocean;
197 
198  // grounding lines and calving fronts
199  if ((grounded(M_ij) and floating_ice(M_n)) or
200  (floating_ice(M_ij) and grounded(M_n)) or
201  (floating_ice(M_ij) and ice_free_ocean(M_n))) {
202  return 0;
203  }
204 
205  // fjord walls, nunataks, headwalls
206  if ((icy(M_ij) and ice_free(M_n) and h_n > h_ij) or
207  (ice_free(M_ij) and icy(M_n) and h_ij > h_n)) {
208  return 0;
209  }
210 
211  // This condition has to match the one used to implement the calving front stress
212  // boundary condition in SSAFD::assemble_rhs().
213  if (margin_bc and
214  ((icy(M_ij) and ice_free(M_n)) or
215  (ice_free(M_ij) and icy(M_n)))) {
216  return 0;
217  }
218 
219  // boundaries of the "no model" area
220  if ((N_ij == 0 and N_n == 1) or (N_ij == 1 and N_n == 0)) {
221  return 0;
222  }
223 
224  return 1;
225 }
226 
227 //! \brief Compute the gravitational driving stress.
228 /*!
229 Computes the gravitational driving stress at the base of the ice:
230 \f[ \tau_d = - \rho g H \nabla h \f]
231  */
232 void SSA::compute_driving_stress(const array::Scalar &ice_thickness,
233  const array::Scalar1 &surface_elevation,
234  const array::CellType1 &cell_type,
235  const array::Scalar1 *no_model_mask,
236  array::Vector &result) const {
237 
238  using mask::ice_free_ocean;
239  using mask::floating_ice;
240 
241  bool cfbc = m_config->get_flag("stress_balance.calving_front_stress_bc");
242  bool surface_gradient_inward = m_config->get_flag("stress_balance.ssa.compute_surface_gradient_inward");
243 
244  double
245  dx = m_grid->dx(),
246  dy = m_grid->dy();
247 
248  array::AccessScope list{&surface_elevation, &cell_type, &ice_thickness, &result};
249 
250  if (no_model_mask) {
251  list.add(*no_model_mask);
252  }
253 
254  for (auto p = m_grid->points(); p; p.next()) {
255  const int i = p.i(), j = p.j();
256 
257  const double pressure = m_EC->pressure(ice_thickness(i, j)); // FIXME issue #15
258  if (pressure <= 0.0) {
259  result(i, j) = 0.0;
260  continue;
261  }
262 
263  // Special case for verification tests.
264  if (surface_gradient_inward) {
265  double
266  h_x = diff_x_p(surface_elevation, i, j),
267  h_y = diff_y_p(surface_elevation, i, j);
268  result(i, j) = - pressure * Vector2d(h_x, h_y);
269  continue;
270  }
271 
272  // To compute the x-derivative we use
273  //
274  // * away from the grounding line, ice margins, and no_model mask transitions -- 2nd
275  // order centered difference
276  //
277  // * at the grounded cell near the grounding line -- 1st order
278  // one-sided difference using the grounded neighbor
279  //
280  // * at the floating cell near the grounding line -- 1st order
281  // one-sided difference using the floating neighbor
282  //
283  // All these cases can be combined by writing h_x as the weighted
284  // average of one-sided differences, with weights of 0 if a finite
285  // difference is not used and 1 if it is.
286  //
287  // The y derivative is handled the same way.
288 
289  auto M = cell_type.star(i, j);
290  auto h = surface_elevation.star(i, j);
291  stencils::Star<int> N(0);
292 
293  if (no_model_mask) {
294  N = no_model_mask->star_int(i, j);
295  }
296 
297  // x-derivative
298  double h_x = 0.0;
299  {
300  double
301  west = weight(cfbc, M.c, M.w, h.c, h.w, N.c, N.w),
302  east = weight(cfbc, M.c, M.e, h.c, h.e, N.c, N.e);
303 
304  if (east + west > 0) {
305  h_x = 1.0 / ((west + east) * dx) * (west * (h.c - h.w) + east * (h.e - h.c));
306  if (floating_ice(M.c) and (ice_free_ocean(M.e) or ice_free_ocean(M.w))) {
307  // at the ice front: use constant extrapolation to approximate the value outside
308  // the ice extent (see the notes in the manual)
309  h_x /= 2.0;
310  }
311  } else {
312  h_x = 0.0;
313  }
314  }
315 
316  // y-derivative
317  double h_y = 0.0;
318  {
319  double
320  south = weight(cfbc, M.c, M.s, h.c, h.s, N.c, N.s),
321  north = weight(cfbc, M.c, M.n, h.c, h.n, N.c, N.n);
322 
323  if (north + south > 0) {
324  h_y = 1.0 / ((south + north) * dy) * (south * (h.c - h.s) + north * (h.n - h.c));
325  if (floating_ice(M.c) and (ice_free_ocean(M.s) or ice_free_ocean(M.n))) {
326  // at the ice front: use constant extrapolation to approximate the value outside
327  // the ice extent
328  h_y /= 2.0;
329  }
330  } else {
331  h_y = 0.0;
332  }
333  }
334 
335  result(i, j) = - pressure * Vector2d(h_x, h_y);
336  }
337 }
338 
339 
340 /*!
341  * Estimate velocity at ice-free cells near the ice margin using interpolation from
342  * immediate neighbors that are icy.
343  *
344  * This is used to improve the initial guess of ice viscosity at marginal locations when
345  * ice advances: otherwise we would use the *zero* velocity (if CFBC is "on"), and that is
346  * a poor estimate at best.
347  *
348  * Note: icy cells of `velocity` are treated as read-only, and ice-free marginal cells are
349  * write-only. This means that it's okay for `velocity` to be a input-output argument: we
350  * don't use of the values modified by this method.
351  */
353  array::Vector1 &velocity) const {
354  array::AccessScope list{&cell_type, &velocity};
355 
356  for (auto p = m_grid->points(); p; p.next()) {
357  const int i = p.i(), j = p.j();
358 
359  if (cell_type.ice_free(i, j) and cell_type.next_to_ice(i, j)) {
360 
361  auto M = cell_type.star(i, j);
362  auto vel = velocity.star(i, j);
363 
364  Vector2d sum{0.0, 0.0};
365  int N = 0;
366  for (auto d : {North, East, South, West}) {
367  if (mask::icy(M[d])) {
368  sum += vel[d];
369  ++N;
370  }
371  }
372  velocity(i, j) = sum / std::max(N, 1);
373  }
374  }
376 }
377 
378 
379 std::string SSA::stdout_report() const {
380  return m_stdout_ssa;
381 }
382 
383 
384 //! \brief Set the initial guess of the SSA velocity.
386  m_velocity.copy_from(guess);
387 }
388 
390  return m_taud;
391 }
392 
393 
394 void SSA::define_model_state_impl(const File &output) const {
396 }
397 
398 void SSA::write_model_state_impl(const File &output) const {
399  m_velocity.write(output);
400 }
401 
404 
405  // replace these diagnostics
406  result["taud"] = Diagnostic::Ptr(new SSA_taud(this));
407  result["taud_mag"] = Diagnostic::Ptr(new SSA_taud_mag(this));
408 
409  return result;
410 }
411 
413  : Diag<SSA>(m) {
414 
415  // set metadata:
416  m_vars = { { m_sys, "taud_x" }, { m_sys, "taud_y" } };
417 
418  m_vars[0].long_name("X-component of the driving shear stress at the base of ice");
419  m_vars[1].long_name("Y-component of the driving shear stress at the base of ice");
420 
421  for (auto &v : m_vars) {
422  v.units("Pa");
423  v["comment"] = "this is the driving stress used by the SSA solver";
424  }
425 }
426 
427 std::shared_ptr<array::Array> SSA_taud::compute_impl() const {
428 
429  auto result = allocate<array::Vector>("taud");
430 
431  result->copy_from(model->driving_stress());
432 
433  return result;
434 }
435 
437  : Diag<SSA>(m) {
438 
439  // set metadata:
440  m_vars = { { m_sys, "taud_mag" } };
441 
442  m_vars[0].long_name("magnitude of the driving shear stress at the base of ice").units("Pa");
443  m_vars[0]["comment"] = "this is the magnitude of the driving stress used by the SSA solver";
444 }
445 
446 std::shared_ptr<array::Array> SSA_taud_mag::compute_impl() const {
447  auto result = allocate<array::Scalar>("taud_mag");
448 
449  compute_magnitude(model->driving_stress(), *result);
450 
451  return result;
452 }
453 
454 } // end of namespace stressbalance
455 } // end of namespace pism
#define ICE_GOLDSBY_KOHLSTEDT
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
double get_number(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
A class for storing and accessing PISM configuration flags and parameters.
const SSA * model
Definition: Diagnostic.hh:172
A template derived from Diagnostic, adding a "Model".
Definition: Diagnostic.hh:166
const units::System::Ptr m_sys
the unit system
Definition: Diagnostic.hh:116
std::vector< SpatialVariableMetadata > m_vars
metadata corresponding to NetCDF variables
Definition: Diagnostic.hh:120
std::shared_ptr< Diagnostic > Ptr
Definition: Diagnostic.hh:65
VariableLookupData find_variable(const std::string &short_name, const std::string &std_name) const
Find a variable using its standard name and/or short name.
Definition: File.cc:361
unsigned int nrecords() const
Get the number of records. Uses the length of an unlimited dimension.
Definition: File.cc:313
High-level PISM I/O class.
Definition: File.hh:56
void compute_mask(const array::Scalar &sea_level, const array::Scalar &bed, const array::Scalar &thickness, array::Scalar &result) const
Definition: Mask.cc:36
void set_icefree_thickness(double threshold)
Definition: Mask.hh:81
array::Scalar1 sea_level_elevation
Definition: Geometry.hh:48
array::Scalar2 ice_thickness
Definition: Geometry.hh:51
array::Scalar2 bed_elevation
Definition: Geometry.hh:47
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
VariableMetadata & set_name(const std::string &name)
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
This class represents a 2D vector field (such as ice velocity) at a certain grid point.
Definition: Vector2d.hh:29
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
void add(double alpha, const Array2D< T > &x)
Definition: Array2D.hh:65
void read(const std::string &filename, unsigned int time)
Definition: Array.cc:809
void define(const File &file, io::Type default_type) const
Define variables corresponding to an Array in a file opened using file.
Definition: Array.cc:540
void write(const std::string &filename) const
Definition: Array.cc:800
void set(double c)
Result: v[j] <- c for all j.
Definition: Array.cc:707
std::shared_ptr< petsc::DM > dm() const
Definition: Array.cc:353
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
bool ice_free(int i, int j) const
Definition: CellType.hh:54
stencils::Star< int > star_int(int i, int j) const
Definition: Scalar.hh:72
std::shared_ptr< FlowLaw > create()
void remove(const std::string &name)
const array::Scalar * basal_yield_stress
double get_notional_strength() const
Returns strength = (viscosity times thickness).
Definition: SSA.cc:61
void set_min_thickness(double my_min_thickness)
Set minimum thickness to trigger use of extension.
Definition: SSA.cc:50
SSAStrengthExtension(const Config &c)
Definition: SSA.cc:34
double get_min_thickness() const
Returns minimum thickness to trigger use of extension.
Definition: SSA.cc:66
void set_notional_strength(double my_nuH)
Set strength = (viscosity times thickness).
Definition: SSA.cc:41
Gives an extension coefficient to maintain ellipticity of SSA where ice is thin.
Definition: SSA.hh:57
virtual std::shared_ptr< array::Array > compute_impl() const
Definition: SSA.cc:446
SSA_taud_mag(const SSA *m)
Definition: SSA.cc:436
Computes the magnitude of the driving shear stress at the base of ice (diagnostically).
SSA_taud(const SSA *m)
Definition: SSA.cc:412
virtual std::shared_ptr< array::Array > compute_impl() const
Definition: SSA.cc:427
Computes the driving shear stress at the base of ice (diagnostically).
void set_initial_guess(const array::Vector &guess)
Set the initial guess of the SSA velocity.
Definition: SSA.cc:385
virtual void update(const Inputs &inputs, bool full_update)
Update the SSA solution.
Definition: SSA.cc:152
virtual DiagnosticList diagnostics_impl() const
Definition: SSA.cc:402
virtual void solve(const Inputs &inputs)=0
SSA(std::shared_ptr< const Grid > g)
Definition: SSA.cc:71
virtual void compute_driving_stress(const array::Scalar &ice_thickness, const array::Scalar1 &surface_elevation, const array::CellType1 &cell_type, const array::Scalar1 *no_model_mask, array::Vector &result) const
Compute the gravitational driving stress.
Definition: SSA.cc:232
array::Vector m_velocity_global
Definition: SSA.hh:150
void extrapolate_velocity(const array::CellType1 &cell_type, array::Vector1 &velocity) const
Definition: SSA.cc:352
array::Vector m_taud
Definition: SSA.hh:144
virtual void define_model_state_impl(const File &output) const
The default (empty implementation).
Definition: SSA.cc:394
SSAStrengthExtension * strength_extension
Definition: SSA.hh:115
virtual std::string stdout_report() const
Produce a report string for the standard output.
Definition: SSA.cc:379
std::string m_stdout_ssa
Definition: SSA.hh:146
std::shared_ptr< petsc::DM > m_da
Definition: SSA.hh:149
array::CellType2 m_mask
Definition: SSA.hh:143
const array::Vector & driving_stress() const
Definition: SSA.cc:389
virtual void write_model_state_impl(const File &output) const
The default (empty implementation).
Definition: SSA.cc:398
virtual void init_impl()
Initialize a generic regular-grid SSA solver.
Definition: SSA.cc:121
PISM's SSA solver.
Definition: SSA.hh:110
const array::Vector1 & velocity() const
Get the thickness-advective 2D velocity.
void compute_basal_frictional_heating(const array::Vector &velocity, const array::Scalar &tauc, const array::CellType &mask, array::Scalar &result) const
Compute the basal frictional heating.
virtual DiagnosticList diagnostics_impl() const
std::shared_ptr< rheology::FlowLaw > m_flow_law
Shallow stress balance (such as the SSA).
#define PISM_ERROR_LOCATION
double max(const array::Scalar &input)
Finds maximum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:165
double sum(const array::Scalar &input)
Sums up all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:150
double diff_x_p(const array::Scalar &array, int i, int j)
Returns the x-derivative at i,j approximated using centered finite differences. Respects grid periodi...
Definition: Scalar.cc:108
void compute_magnitude(const array::Vector &input, array::Scalar &result)
Definition: Scalar.cc:66
double diff_y_p(const array::Scalar &array, int i, int j)
Returns the y-derivative at i,j approximated using centered finite differences. Respects grid periodi...
Definition: Scalar.cc:129
@ PISM_GUESS
Definition: IO_Flags.hh:56
@ PISM_READONLY
open an existing file for reading only
Definition: IO_Flags.hh:72
@ PISM_DOUBLE
Definition: IO_Flags.hh:52
bool icy(int M)
Ice-filled cell (grounded or floating).
Definition: Mask.hh:48
bool ice_free_ocean(int M)
Definition: Mask.hh:61
bool grounded(int M)
Grounded cell (grounded ice or ice-free).
Definition: Mask.hh:44
bool ice_free(int M)
Ice-free cell (grounded or ocean).
Definition: Mask.hh:58
bool floating_ice(int M)
Definition: Mask.hh:54
static int weight(int M_ij, int M_n, double h_ij, double h_n)
InputOptions process_input_options(MPI_Comm com, Config::ConstPtr config)
Definition: Component.cc:43
static const double g
Definition: exactTestP.cc:36
@ INIT_RESTART
Definition: Component.hh:56
std::map< std::string, Diagnostic::Ptr > DiagnosticList
Definition: Diagnostic.hh:125
@ MASK_FLOATING
Definition: Mask.hh:34
@ MASK_ICE_FREE_OCEAN
Definition: Mask.hh:35
@ MASK_ICE_FREE_BEDROCK
Definition: Mask.hh:32
@ MASK_GROUNDED
Definition: Mask.hh:33
@ North
Definition: stencils.hh:24
@ East
Definition: stencils.hh:24
@ South
Definition: stencils.hh:24
@ West
Definition: stencils.hh:24
InitializationType type
initialization type
Definition: Component.hh:61
std::string filename
name of the input file (if applicable)
Definition: Component.hh:63
Star stencil points (in the map-plane).
Definition: stencils.hh:30