Loading [MathJax]/extensions/tex2jax.js
PISM, A Parallel Ice Sheet Model 2.2.1-cd005eec8 committed by Constantine Khrulev on 2025-03-07
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
MohrCoulombYieldStress.cc
Go to the documentation of this file.
1// Copyright (C) 2004--2023, 2025 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/basalstrength/MohrCoulombYieldStress.hh"
20#include "pism/basalstrength/MohrCoulombPointwise.hh"
21
22#include "pism/util/Grid.hh"
23#include "pism/util/error_handling.hh"
24#include "pism/util/io/File.hh"
25#include "pism/util/MaxTimestep.hh"
26#include "pism/util/pism_utilities.hh"
27#include "pism/util/Time.hh"
28#include "pism/util/array/CellType.hh"
29#include "pism/util/array/Forcing.hh"
30#include "pism/geometry/Geometry.hh"
31#include "pism/coupler/util/options.hh" // ForcingOptions
32
33namespace pism {
34
35//! \file MohrCoulombYieldStress.cc Process model which computes pseudo-plastic yield stress for the subglacial layer.
36
37/*! \file MohrCoulombYieldStress.cc
38The output variable of this submodel is `tauc`, the pseudo-plastic yield stress
39field that is used in the ShallowStressBalance objects. This quantity is
40computed by the Mohr-Coulomb criterion [\ref SchoofTill], but using an empirical
41relation between the amount of water in the till and the effective pressure
42of the overlying glacier resting on the till [\ref Tulaczyketal2000].
43
44The "dry" strength of the till is a state variable which is private to
45the submodel, namely `tillphi`. Its initialization is nontrivial: either the
46`-topg_to_phi` heuristic is used or inverse modeling can be used. (In the
47latter case `tillphi` can be read-in at the beginning of the run. Currently
48`tillphi` does not evolve during the run.)
49
50The effective pressure is derived from the till (pore) water amount (the effective water
51layer thickness). Then the effective pressure is combined with tillphi to compute an
52updated `tauc` by the Mohr-Coulomb criterion.
53
54This submodel is inactive in floating areas.
55*/
56
57
58/*!
59The pseudo-plastic till basal resistance model is governed by this power law
60equation,
61 @f[ \tau_b = - \frac{\tau_c}{|\mathbf{U}|^{1-q} U_{\mathtt{th}}^q} \mathbf{U}, @f]
62where @f$\tau_b=(\tau_{(b)x},\tau_{(b)y})@f$ is the basal shear stress and
63@f$U=(u,v)@f$ is the sliding velocity.
64
65We call the scalar field @f$\tau_c(t,x,y)@f$ the *yield stress* even when
66the power @f$q@f$ is not zero; when that power is zero the formula describes
67a plastic material with an actual yield stress. The constant
68@f$U_{\mathtt{th}}@f$ is the *threshold speed*, and @f$q@f$ is the *pseudo*
69*plasticity exponent*. The current class computes this yield stress field.
70See also IceBasalResistancePlasticLaw::drag().
71
72The strength of the saturated till material, the yield stress, is modeled by a
73Mohr-Coulomb relation [\ref Paterson, \ref SchoofStream],
74 @f[ \tau_c = c_0 + (\tan \varphi) N_{till}, @f]
75where @f$N_{till}@f$ is the effective pressure of the glacier on the mineral
76till.
77
78The determination of the till friction angle @f$\varphi(x,y)@f$ is important.
79It is assumed in this default model to be a time-independent factor which
80describes the strength of the unsaturated "dry" (mineral) till material. Thus
81it is assumed to change more slowly than the till water pressure, and it follows
82that it changes more slowly than the yield stress and the basal shear stress.
83
84Option `-topg_to_phi` causes call to topg_to_phi() at the beginning of the run.
85This determines the map of @f$\varphi(x,y)@f$. If this option is note given,
86the current method leaves `tillphi` unchanged, and thus either in its
87read-in-from-file state or with a default constant value from the config file.
88*/
89MohrCoulombYieldStress::MohrCoulombYieldStress(std::shared_ptr<const Grid> grid)
90 : YieldStress(grid),
91 m_till_phi(m_grid, "tillphi") {
92
93 m_name = "Mohr-Coulomb yield stress model";
94
96 .long_name("friction angle for till under grounded ice sheet")
97 .units("degrees")
99
100 // in this model; need not be time-independent in general
101
102 {
103 std::string hydrology_tillwat_max = "hydrology.tillwat_max";
104 bool till_is_present = m_config->get_number(hydrology_tillwat_max) > 0.0;
105
106 if (not till_is_present) {
108 "The Mohr-Coulomb yield stress model cannot be used without till.\n"
109 "Reset %s or choose a different yield stress model.",
110 hydrology_tillwat_max.c_str());
111 }
112 }
113
114 auto delta_file = m_config->get_string("basal_yield_stress.mohr_coulomb.delta.file");
115
116 if (not delta_file.empty()) {
117 ForcingOptions opt(*m_grid->ctx(), "basal_yield_stress.mohr_coulomb.delta");
118
119 unsigned int buffer_size = m_config->get_number("input.forcing.buffer_size");
120
122
123 m_delta = std::make_shared<array::Forcing>(m_grid,
124 file,
125 "mohr_coulomb_delta",
126 "", // no standard name
127 buffer_size,
128 opt.periodic, LINEAR);
129 m_delta->metadata()
130 .long_name("minimum effective pressure on till as a fraction of overburden pressure")
131 .units("1");
132 }
133}
134
135void MohrCoulombYieldStress::restart_impl(const File &input_file, int record) {
136 m_basal_yield_stress.read(input_file, record);
137 m_till_phi.read(input_file, record);
138}
139
140
141//! Initialize the pseudo-plastic till mechanical model.
143 const YieldStressInputs &inputs) {
144
145 auto tauc_to_phi_file = m_config->get_string("basal_yield_stress.mohr_coulomb.tauc_to_phi.file");
146
147 if (not tauc_to_phi_file.empty()) {
148 m_basal_yield_stress.regrid(tauc_to_phi_file, io::Default::Nil());
149
150 m_log->message(2,
151 " Will compute till friction angle (tillphi) as a function"
152 " of the yield stress (tauc)...\n");
153
155 *inputs.till_water_thickness,
156 inputs.geometry->ice_thickness,
157 inputs.geometry->cell_type,
158 m_till_phi);
159
160 } else if (m_config->get_flag("basal_yield_stress.mohr_coulomb.topg_to_phi.enabled")) {
161
162 m_log->message(2, " creating till friction angle map from bed elevation...\n");
163
164 if (input_file.variable_exists(m_till_phi.metadata().get_name())) {
165 // till_phi is present in the input file
166 m_log->message(2,
167 "PISM WARNING: -topg_to_phi computation will override the '%s' field\n"
168 " present in the input file '%s'!\n",
169 m_till_phi.metadata().get_name().c_str(), input_file.name().c_str());
170 }
171
173
174 } else {
175 double till_phi_default = m_config->get_number("basal_yield_stress.mohr_coulomb.till_phi_default");
176 m_till_phi.regrid(input_file, io::Default(till_phi_default));
177 }
178
179 finish_initialization(inputs);
180}
181
183 double till_phi_default = m_config->get_number("basal_yield_stress.mohr_coulomb.till_phi_default");
184 m_till_phi.set(till_phi_default);
185
186 finish_initialization(inputs);
187}
188
189/*!
190 * Finish initialization after bootstrapping or initializing using constants.
191 */
193 // regrid if requested, regardless of how initialized
195
196 if (m_delta) {
197 ForcingOptions opt(*m_grid->ctx(), "basal_yield_stress.mohr_coulomb.delta");
198
199 m_delta->init(opt.filename, opt.periodic);
200 }
201
202 // We use a short time step length because we can get away with it here, but we can
203 // probably do better...
204 this->update(inputs, time().current(), 1.0 /* one second time step */);
205}
206
208 (void) t;
209
210 if (m_delta) {
211 auto dt = m_delta->max_timestep(t);
212
213 if (dt.finite()) {
214 return MaxTimestep(dt.value(), name());
215 }
216 }
217
218 return MaxTimestep(name());
219}
220
224
229
232 m_till_phi.write(output);
233}
234
235//! Update the till yield stress for use in the pseudo-plastic till basal stress
236//! model. See also IceBasalResistancePlasticLaw.
237/*!
238Updates yield stress @f$ \tau_c @f$ based on modeled till water layer thickness
239from a Hydrology object. We implement the Mohr-Coulomb criterion allowing
240a (typically small) till cohesion @f$ c_0 @f$
241and by expressing the coefficient as the tangent of a till friction angle
242 @f$ \varphi @f$ :
243 @f[ \tau_c = c_0 + (\tan \varphi) N_{till}. @f]
244See [@ref Paterson] table 8.1 regarding values.
245
246The effective pressure on the till is empirically-related
247to the amount of water in the till. We use this formula derived from
248[@ref Tulaczyketal2000] and documented in [@ref BuelervanPeltDRAFT]:
249
250@f[ N_{till} = \min\left\{P_o, N_0 \left(\frac{\delta P_o}{N_0}\right)^s 10^{(e_0/C_c) (1 - s)}\right\} @f]
251
252where @f$ s = W_{till} / W_{till}^{max} @f$, @f$ W_{till}^{max} @f$ =`hydrology_tillwat_max`,
253@f$ \delta @f$ =`basal_yield_stress.mohr_coulomb.till_effective_fraction_overburden`, @f$ P_o @f$ is the
254overburden pressure, @f$ N_0 @f$ =`basal_yield_stress.mohr_coulomb.till_reference_effective_pressure` is a
255reference effective pressure, @f$ e_0 @f$ =`basal_yield_stress.mohr_coulomb.till_reference_void_ratio` is the void ratio
256at the reference effective pressure, and @f$ C_c @f$ =`basal_yield_stress.mohr_coulomb.till_compressibility_coefficient`
257is the coefficient of compressibility of the till. Constants @f$ N_0, e_0, C_c @f$ are
258found by [@ref Tulaczyketal2000] from laboratory experiments on samples of
259till.
260
261If `basal_yield_stress.add_transportable_water` is yes then @f$ s @f$ in the above formula
262becomes @f$ s = (W + W_{till}) / W_{till}^{max} @f$,
263that is, the water amount is the sum @f$ W+W_{till} @f$.
264 */
266 double t, double dt) {
267 (void) t;
268 (void) dt;
269
270 bool slippery_grounding_lines = m_config->get_flag("basal_yield_stress.slippery_grounding_lines"),
271 add_transportable_water = m_config->get_flag("basal_yield_stress.add_transportable_water");
272
273 const double
274 ice_density = m_config->get_number("constants.ice.density"),
275 standard_gravity = m_config->get_number("constants.standard_gravity");
276
277 const double high_tauc = m_config->get_number("basal_yield_stress.ice_free_bedrock"),
278 W_till_max = m_config->get_number("hydrology.tillwat_max"),
279 delta = m_config->get_number("basal_yield_stress.mohr_coulomb.till_effective_fraction_overburden"),
280 tlftw = m_config->get_number("basal_yield_stress.mohr_coulomb.till_log_factor_transportable_water");
281
283
284 const array::Scalar
285 &W_till = *inputs.till_water_thickness,
286 &W_subglacial = *inputs.subglacial_water_thickness,
287 &ice_thickness = inputs.geometry->ice_thickness;
288
289 const auto &cell_type = inputs.geometry->cell_type;
290 const auto &bed_topography = inputs.geometry->bed_elevation;
291 const auto &sea_level = inputs.geometry->sea_level_elevation;
292
293 array::AccessScope list{&W_till, &m_till_phi, &m_basal_yield_stress, &cell_type,
294 &bed_topography, &sea_level, &ice_thickness};
295
296 if (add_transportable_water) {
297 list.add(W_subglacial);
298 }
299
300 if (m_delta) {
301 m_delta->update(t, dt);
302 m_delta->average(t, dt);
303 list.add(*m_delta);
304 }
305
306 for (auto p = m_grid->points(); p; p.next()) {
307 const int i = p.i(), j = p.j();
308
309 if (cell_type.ice_free(i, j)) {
310 m_basal_yield_stress(i, j) = high_tauc; // large yield stress if ice-free
311 } else { // grounded and there is some ice
312
313 // user can ask that marine grounding lines get special treatment
314 double water = W_till(i,j); // usual case
315
316 if (slippery_grounding_lines and
317 bed_topography(i, j) <= sea_level(i, j) and
318 (cell_type.next_to_floating_ice(i, j) or cell_type.next_to_ice_free_ocean(i, j))) {
319 water = W_till_max;
320 } else if (add_transportable_water) {
321 water = W_till(i, j) + tlftw * log(1.0 + W_subglacial(i, j) / tlftw);
322 }
323
324 double P_overburden = ice_density * standard_gravity * ice_thickness(i, j);
325
326 m_basal_yield_stress(i, j) = mc.yield_stress(m_delta ? (*m_delta)(i, j) : delta,
327 P_overburden, water, m_till_phi(i, j));
328 }
329 }
330
332}
333
334//! Computes the till friction angle phi as a piecewise linear function of bed elevation, according to user options.
335/*!
336Computes the till friction angle \f$\phi(x,y)\f$ at a location as the following
337increasing, piecewise-linear function of the bed elevation \f$b(x,y)\f$. Let
338 \f[ M = (\phi_{\text{max}} - \phi_{\text{min}}) / (b_{\text{max}} - b_{\text{min}}) \f]
339be the slope of the nontrivial part. Then
340 \f[ \phi(x,y) = \begin{cases}
341 \phi_{\text{min}}, & b(x,y) \le b_{\text{min}}, \\
342 \phi_{\text{min}} + (b(x,y) - b_{\text{min}}) \,M,
343 & b_{\text{min}} < b(x,y) < b_{\text{max}}, \\
344 \phi_{\text{max}}, & b_{\text{max}} \le b(x,y), \end{cases} \f]
345where \f$\phi_{\text{min}}=\f$`phi_min`, \f$\phi_{\text{max}}=\f$`phi_max`,
346\f$b_{\text{min}}=\f$`topg_min`, \f$b_{\text{max}}=\f$`topg_max`.
347
348The default values are vaguely suitable for Antarctica. See src/pism_config.cdl.
349*/
351 array::Scalar &result) {
352
353 const double
354 phi_min = m_config->get_number("basal_yield_stress.mohr_coulomb.topg_to_phi.phi_min"),
355 phi_max = m_config->get_number("basal_yield_stress.mohr_coulomb.topg_to_phi.phi_max"),
356 topg_min = m_config->get_number("basal_yield_stress.mohr_coulomb.topg_to_phi.topg_min"),
357 topg_max = m_config->get_number("basal_yield_stress.mohr_coulomb.topg_to_phi.topg_max");
358
359 m_log->message(2,
360 " till friction angle (phi) is piecewise-linear function of bed elev (topg):\n"
361 " / %5.2f for topg < %.f\n"
362 " phi = | %5.2f + (topg - (%.f)) * (%.2f / %.f) for %.f < topg < %.f\n"
363 " \\ %5.2f for %.f < topg\n",
364 phi_min, topg_min,
365 phi_min, topg_min, phi_max - phi_min, topg_max - topg_min, topg_min, topg_max,
366 phi_max, topg_max);
367
368 if (phi_min >= phi_max) {
370 "invalid -topg_to_phi arguments: phi_min < phi_max is required");
371 }
372
373 if (topg_min >= topg_max) {
375 "invalid -topg_to_phi arguments: topg_min < topg_max is required");
376 }
377
378 const double slope = (phi_max - phi_min) / (topg_max - topg_min);
379
380 array::AccessScope list{&bed_topography, &result};
381
382 for (auto p = m_grid->points(); p; p.next()) {
383 const int i = p.i(), j = p.j();
384 const double bed = bed_topography(i, j);
385
386 if (bed <= topg_min) {
387 result(i, j) = phi_min;
388 } else if (bed >= topg_max) {
389 result(i, j) = phi_max;
390 } else {
391 result(i, j) = phi_min + (bed - topg_min) * slope;
392 }
393 }
394
395 // communicate ghosts so that the tauc computation can be performed locally
396 // (including ghosts of tauc, that is)
397 result.update_ghosts();
398}
399
400/*!
401 * Compute the till friction angle in grounded areas using available basal yield stress,
402 * till water thickness, and overburden pressure.
403 *
404 * This is the inverse of the formula used by `update_impl()`.
405 */
407 const array::Scalar &till_water_thickness,
408 const array::Scalar &ice_thickness,
409 const array::CellType &cell_type,
410 array::Scalar &result) {
411
413
414 double
415 ice_density = m_config->get_number("constants.ice.density"),
416 standard_gravity = m_config->get_number("constants.standard_gravity");
417
418 double
419 delta = m_config->get_number("basal_yield_stress.mohr_coulomb.till_effective_fraction_overburden");
420
421 const array::Scalar
422 &W_till = till_water_thickness;
423
424 array::AccessScope list{&cell_type, &basal_yield_stress, &W_till, &ice_thickness, &result};
425
426 for (auto p = m_grid->points(); p; p.next()) {
427 const int i = p.i(), j = p.j();
428
429 if (cell_type.ocean(i, j) or cell_type.ice_free(i, j)) {
430 // no change
431 } else { // grounded and there is some ice
432 double P_overburden = ice_density * standard_gravity * ice_thickness(i, j);
433
434 result(i, j) = mc.till_friction_angle(delta, P_overburden, W_till(i, j), basal_yield_stress(i, j));
435 }
436 }
437
438 result.update_ghosts();
439}
440
445
446} // end of namespace pism
const Time & time() const
Definition Component.cc:109
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
void regrid(const std::string &module_name, array::Array &variable, RegriddingFlag flag=NO_REGRID_WITHOUT_REGRID_VARS)
Definition Component.cc:159
static Ptr wrap(const T &input)
bool variable_exists(const std::string &short_name) const
Checks if a variable exists.
Definition File.cc:378
std::string name() const
Definition File.cc:274
High-level PISM I/O class.
Definition File.hh:55
array::Scalar1 sea_level_elevation
Definition Geometry.hh:48
array::CellType2 cell_type
Definition Geometry.hh:55
array::Scalar2 ice_thickness
Definition Geometry.hh:51
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...
double yield_stress(double delta, double P_overburden, double water_thickness, double phi) const
double till_friction_angle(double delta, double P_overburden, double water_thickness, double yield_stress) const
void init_impl(const YieldStressInputs &inputs)
void restart_impl(const File &input_file, int record)
void update_impl(const YieldStressInputs &inputs, double t, double dt)
DiagnosticList diagnostics_impl() const
MohrCoulombYieldStress(std::shared_ptr< const Grid > g)
void set_till_friction_angle(const array::Scalar &input)
void write_model_state_impl(const File &output) const
The default (empty implementation).
void define_model_state_impl(const File &output) const
void finish_initialization(const YieldStressInputs &inputs)
MaxTimestep max_timestep_impl(double t) const
std::shared_ptr< array::Forcing > m_delta
void till_friction_angle(const array::Scalar &bed_topography, array::Scalar &result)
Computes the till friction angle phi as a piecewise linear function of bed elevation,...
void bootstrap_impl(const File &input_file, const YieldStressInputs &inputs)
Initialize the pseudo-plastic till mechanical model.
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
double get_number(const std::string &name) const
Get a single-valued scalar attribute.
std::string get_name() const
VariableMetadata & set_time_independent(bool flag)
const array::Scalar * till_water_thickness
const array::Scalar * subglacial_water_thickness
const Geometry * geometry
std::string m_name
void update(const YieldStressInputs &inputs, double t, double dt)
std::string name() const
DiagnosticList diagnostics_impl() const
array::Scalar2 m_basal_yield_stress
The PISM basal yield stress model interface (virtual base class)
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition Array.hh:64
void copy_from(const Array2D< T > &source)
Definition Array2D.hh:73
void add(double alpha, const Array2D< T > &x)
Definition Array2D.hh:65
void read(const std::string &filename, unsigned int time)
Definition Array.cc:731
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:463
void write(const std::string &filename) const
Definition Array.cc:722
void set(double c)
Result: v[j] <- c for all j.
Definition Array.cc:629
void regrid(const std::string &filename, io::Default default_value)
Definition Array.cc:736
void update_ghosts()
Updates ghost points.
Definition Array.cc:615
SpatialVariableMetadata & metadata(unsigned int N=0)
Returns a reference to the SpatialVariableMetadata object containing metadata for the compoment N.
Definition Array.cc:476
bool ice_free(int i, int j) const
Definition CellType.hh:54
bool ocean(int i, int j) const
Definition CellType.hh:34
"Cell type" mask. Adds convenience methods to array::Scalar.
Definition CellType.hh:30
static Default Nil()
Definition IO_Flags.hh:93
#define PISM_ERROR_LOCATION
@ PISM_NETCDF3
Definition IO_Flags.hh:57
@ PISM_READONLY
open an existing file for reading only
Definition IO_Flags.hh:68
@ PISM_DOUBLE
Definition IO_Flags.hh:52
std::map< std::string, Diagnostic::Ptr > DiagnosticList
T combine(const T &a, const T &b)
std::string filename
Definition options.hh:33