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
FractureDensity.cc
Go to the documentation of this file.
1/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024 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
20#include <algorithm> // std::min, std::max
21#include <cmath> // std::pow
22
23#include "pism/fracturedensity/FractureDensity.hh"
24#include "pism/geometry/Geometry.hh"
25#include "pism/stressbalance/StressBalance.hh"
26#include "pism/util/pism_utilities.hh"
27
28namespace pism {
29
30FractureDensity::FractureDensity(std::shared_ptr<const Grid> grid,
31 std::shared_ptr<const rheology::FlowLaw> flow_law)
32 : Component(grid),
33 m_density(grid, "fracture_density"),
34 m_density_new(grid, "new_fracture_density"),
35 m_growth_rate(grid, "fracture_growth_rate"),
36 m_healing_rate(grid, "fracture_healing_rate"),
37 m_flow_enhancement(grid, "fracture_flow_enhancement"),
38 m_age(grid, "fracture_age"),
39 m_age_new(grid, "new_fracture_age"),
40 m_toughness(grid, "fracture_toughness"),
41 m_strain_rates(grid, "strain_rates", array::WITHOUT_GHOSTS),
42 m_deviatoric_stresses(grid, "sigma", array::WITHOUT_GHOSTS, 3),
43 m_velocity(grid, "ghosted_velocity"),
44 m_flow_law(flow_law) {
45
46 m_density.metadata(0).long_name("fracture density in ice shelf").units("1");
47 m_density.metadata()["valid_max"] = { 1.0 };
48 m_density.metadata()["valid_min"] = { 0.0 };
49
51 .long_name("fracture growth rate")
52 .units("second^-1");
53 m_growth_rate.metadata()["valid_min"] = { 0.0 };
54
56 .long_name("fracture healing rate")
57 .units("second^-1");
58
60 .long_name("fracture-induced flow enhancement");
61
63 .long_name("age since fracturing")
64 .units("seconds");
65
67 .long_name("fracture toughness")
68 .units("Pa");
69
70 m_strain_rates.metadata(0).set_name("eigen1");
71 m_strain_rates.metadata(0)
72 .long_name("major principal component of horizontal strain-rate")
73 .units("second^-1");
74
75 m_strain_rates.metadata(1).set_name("eigen2");
76 m_strain_rates.metadata(1)
77 .long_name("minor principal component of horizontal strain-rate")
78 .units("second^-1");
79
80 m_deviatoric_stresses.metadata(0).set_name("sigma_xx");
81 m_deviatoric_stresses.metadata(0).long_name("deviatoric stress in x direction").units("Pa");
82
83 m_deviatoric_stresses.metadata(1).set_name("sigma_yy");
84 m_deviatoric_stresses.metadata(1).long_name("deviatoric stress in y direction").units("Pa");
85
86 m_deviatoric_stresses.metadata(2).set_name("sigma_xy");
87 m_deviatoric_stresses.metadata(2).long_name("deviatoric shear stress").units("Pa");
88}
89
90void FractureDensity::restart(const File &input_file, int record) {
91 m_log->message(2, "* Restarting the fracture density model from %s...\n",
92 input_file.name().c_str());
93
94 m_density.read(input_file, record);
95 m_age.read(input_file, record);
96
97 regrid("Fracture density model", m_density, REGRID_WITHOUT_REGRID_VARS);
98 regrid("Fracture density model", m_age, REGRID_WITHOUT_REGRID_VARS);
99}
100
101void FractureDensity::bootstrap(const File &input_file) {
102 m_log->message(2, "* Bootstrapping the fracture density model from %s...\n",
103 input_file.name().c_str());
104
105 m_density.regrid(input_file, io::Default(0.0));
106 m_age.regrid(input_file, io::Default(0.0));
107}
108
114
116 m_density.set(0.0);
117 m_age.set(0.0);
118}
119
124
126 m_density.write(output);
127 m_age.write(output);
128}
129
131 const Geometry &geometry,
132 const array::Vector &velocity,
133 const array::Scalar &hardness,
134 const array::Scalar &bc_mask) {
135 using std::pow;
136
137 const double
138 dx = m_grid->dx(),
139 dy = m_grid->dy();
140 const int
141 Mx = m_grid->Mx(),
142 My = m_grid->My();
143
145 &A = m_age;
147 &D = m_density,
148 &D_new = m_density_new,
149 &A_new = m_age_new;
150
151 m_velocity.copy_from(velocity);
152
154 geometry.cell_type,
156
159 hardness,
160 geometry.cell_type,
162
164 &D, &D_new, &geometry.cell_type, &bc_mask, &A, &A_new,
166 &m_toughness, &hardness, &geometry.ice_thickness};
167
168 D_new.copy_from(D);
169
170 //options
171 /////////////////////////////////////////////////////////
172 double soft_residual = m_config->get_number("fracture_density.softening_lower_limit");
173 // assume linear response function: E_fr = (1-(1-soft_residual)*phi) -> 1-phi
174 //
175 // See the following article for more:
176 //
177 // Albrecht, T. / Levermann, A.
178 // Fracture-induced softening for large-scale ice dynamics
179 // 2014-04
180 //
181 // The Cryosphere , Vol. 8, No. 2
182 // Copernicus GmbH
183 // p. 587-605
184 //
185 // doi:10.5194/tc-8-587-2014
186 //
187 // get four options for calculation of fracture density.
188 // 1st: fracture growth constant gamma
189 // 2nd: fracture initiation stress threshold sigma_cr
190 // 3rd: healing rate constant gamma_h
191 // 4th: healing strain rate threshold
192 //
193 // more: T. Albrecht, A. Levermann; Fracture field for large-scale
194 // ice dynamics; (2012), Journal of Glaciology, Vol. 58, No. 207,
195 // 165-176, DOI: 10.3189/2012JoG11J191.
196
197 double gamma = m_config->get_number("fracture_density.gamma");
198 double initThreshold = m_config->get_number("fracture_density.initiation_threshold");
199 double gammaheal = m_config->get_number("fracture_density.gamma_h");
200 double healThreshold = m_config->get_number("fracture_density.healing_threshold");
201
202 m_log->message(3, "PISM-PIK INFO: fracture density is found with parameters:\n"
203 " gamma=%.2f, sigma_cr=%.2f, gammah=%.2f, healing_cr=%.1e and soft_res=%f \n",
204 gamma, initThreshold, gammaheal, healThreshold, soft_residual);
205
206 bool do_fracground = m_config->get_flag("fracture_density.include_grounded_ice");
207
208 double fdBoundaryValue = m_config->get_number("fracture_density.phi0");
209
210 bool constant_healing = m_config->get_flag("fracture_density.constant_healing");
211
212 bool fracture_weighted_healing = m_config->get_flag("fracture_density.fracture_weighted_healing");
213
214 bool max_shear_stress = m_config->get_flag("fracture_density.max_shear_stress");
215
216 bool lefm = m_config->get_flag("fracture_density.lefm");
217
218 bool constant_fd = m_config->get_flag("fracture_density.constant_fd");
219
220 bool fd2d_scheme = m_config->get_flag("fracture_density.fd2d_scheme");
221
222 double glen_exponent = m_flow_law->exponent();
223
224 bool borstad_limit = m_config->get_flag("fracture_density.borstad_limit");
225
226 double minH = m_config->get_number("stress_balance.ice_free_thickness_standard");
227
228 for (auto p = m_grid->points(); p; p.next()) {
229 const int i = p.i(), j = p.j();
230
231 double tempFD = 0.0;
232
233 double u = m_velocity(i, j).u;
234 double v = m_velocity(i, j).v;
235
236 if (fd2d_scheme) {
237 if (u >= dx * v / dy and v >= 0.0) { //1
238 tempFD = u * (D(i, j) - D(i - 1, j)) / dx + v * (D(i - 1, j) - D(i - 1, j - 1)) / dy;
239 } else if (u <= dx * v / dy and u >= 0.0) { //2
240 tempFD = u * (D(i, j - 1) - D(i - 1, j - 1)) / dx + v * (D(i, j) - D(i, j - 1)) / dy;
241 } else if (u >= -dx * v / dy and u <= 0.0) { //3
242 tempFD = -u * (D(i, j - 1) - D(i + 1, j - 1)) / dx + v * (D(i, j) - D(i, j - 1)) / dy;
243 } else if (u <= -dx * v / dy and v >= 0.0) { //4
244 tempFD = -u * (D(i, j) - D(i + 1, j)) / dx + v * (D(i + 1, j) - D(i + 1, j - 1)) / dy;
245 } else if (u <= dx * v / dy and v <= 0.0) { //5
246 tempFD = -u * (D(i, j) - D(i + 1, j)) / dx - v * (D(i + 1, j) - D(i + 1, j + 1)) / dy;
247 } else if (u >= dx * v / dy and u <= 0.0) { //6
248 tempFD = -u * (D(i, j + 1) - D(i + 1, j + 1)) / dx - v * (D(i, j) - D(i, j + 1)) / dy;
249 } else if (u <= -dx * v / dy and u >= 0.0) { //7
250 tempFD = u * (D(i, j + 1) - D(i - 1, j + 1)) / dx - v * (D(i, j) - D(i, j + 1)) / dy;
251 } else if (u >= -dx * v / dy and v <= 0.0) { //8
252 tempFD = u * (D(i, j) - D(i - 1, j)) / dx - v * (D(i - 1, j) - D(i - 1, j + 1)) / dy;
253 } else {
254 m_log->message(3, "######### missing case of angle %f of %f and %f at %d, %d \n",
255 atan(v / u) / M_PI * 180., u * 3e7, v * 3e7, i, j);
256 }
257 } else {
258 tempFD += u * (u < 0 ? D(i + 1, j) - D(i, j) : D(i, j) - D(i - 1, j)) / dx;
259 tempFD += v * (v < 0 ? D(i, j + 1) - D(i, j) : D(i, j) - D(i, j - 1)) / dy;
260 }
261
262 D_new(i, j) -= tempFD * dt;
263
264 //sources /////////////////////////////////////////////////////////////////
265 ///von mises criterion
266
267 double
268 txx = m_deviatoric_stresses(i, j).xx,
269 tyy = m_deviatoric_stresses(i, j).yy,
270 txy = m_deviatoric_stresses(i, j).xy,
271 T1 = 0.5 * (txx + tyy) + sqrt(0.25 * pow(txx - tyy, 2) + pow(txy, 2)), //Pa
272 T2 = 0.5 * (txx + tyy) - sqrt(0.25 * pow(txx - tyy, 2) + pow(txy, 2)), //Pa
273 sigmat = sqrt(pow(T1, 2) + pow(T2, 2) - T1 * T2);
274
275
276 ///max shear stress criterion (more stringent than von mises)
277 if (max_shear_stress) {
278 double maxshear = fabs(T1);
279 maxshear = std::max(maxshear, fabs(T2));
280 maxshear = std::max(maxshear, fabs(T1 - T2));
281
282 sigmat = maxshear;
283 }
284
285 ///lefm mixed-mode criterion
286 if (lefm) {
287 double sigmamu = 0.1; //friction coefficient between crack faces
288
289 double sigmac = 0.64 / M_PI; //initial crack depth 20cm
290
291 double sigmabetatest, sigmanor, sigmatau, Kone, Ktwo, KSI, KSImax = 0.0, sigmatetanull;
292
293 for (int l = 46; l <= 90; ++l) { //optimize for various precursor angles beta
294 sigmabetatest = l * M_PI / 180.0;
295
296 //rist_sammonds99
297 sigmanor = 0.5 * (T1 + T2) - (T1 - T2) * cos(2 * sigmabetatest);
298 sigmatau = 0.5 * (T1 - T2) * sin(2 * sigmabetatest);
299 //shayam_wu90
300 if (sigmamu * sigmanor < 0.0) { //compressive case
301 if (fabs(sigmatau) <= fabs(sigmamu * sigmanor)) {
302 sigmatau = 0.0;
303 } else {
304 if (sigmatau > 0) { //coulomb friction opposing sliding
305 sigmatau += (sigmamu * sigmanor);
306 } else {
307 sigmatau -= (sigmamu * sigmanor);
308 }
309 }
310 }
311
312 //stress intensity factors
313 Kone = sigmanor * sqrt(M_PI * sigmac); //normal
314 Ktwo = sigmatau * sqrt(M_PI * sigmac); //shear
315
316 if (Ktwo == 0.0) {
317 sigmatetanull = 0.0;
318 } else { //eq15 in hulbe_ledoux10 or eq15 shayam_wu90
319 sigmatetanull = -2.0 * atan((sqrt(pow(Kone, 2) + 8.0 * pow(Ktwo, 2)) - Kone) / (4.0 * Ktwo));
320 }
321
322 KSI = cos(0.5 * sigmatetanull) *
323 (Kone * cos(0.5 * sigmatetanull) * cos(0.5 * sigmatetanull) - 0.5 * 3.0 * Ktwo * sin(sigmatetanull));
324 // mode I stress intensity
325
326 KSImax = std::max(KSI, KSImax);
327 }
328 sigmat = KSImax;
329 }
330
331 //////////////////////////////////////////////////////////////////////////////
332
333 // fracture density
334 double fdnew = 0.0;
335 if (borstad_limit) {
336 if (geometry.ice_thickness(i, j) > minH) {
337 // mean parameters from paper
338 double t0 = initThreshold;
339 double kappa = 2.8;
340
341 // effective strain rate
342 double e1 = m_strain_rates(i, j).eigen1;
343 double e2 = m_strain_rates(i, j).eigen2;
344 double ee = sqrt(pow(e1, 2.0) + pow(e2, 2.0) - e1 * e2);
345
346 // threshold for unfractured ice
347 double e0 = pow((t0 / hardness(i, j)), glen_exponent);
348
349 // threshold for fractured ice (exponential law)
350 double ex = exp((e0 - ee) / (e0 * (kappa - 1)));
351
352 // stress threshold for fractures ice
353 double te = t0 * ex;
354
355 // actual effective stress
356 double ts = hardness(i, j) * pow(ee, 1.0 / glen_exponent) * (1 - D_new(i, j));
357
358 // fracture formation if threshold is hit
359 if (ts > te and ee > e0) {
360 // new fracture density:
361 fdnew = 1.0 - (ex * pow((ee / e0), -1 / glen_exponent));
362 D_new(i, j) = fdnew;
363 }
364 }
365 } else {
366 fdnew = gamma * (m_strain_rates(i, j).eigen1 - 0.0) * (1 - D_new(i, j));
367 if (sigmat > initThreshold) {
368 D_new(i, j) += fdnew * dt;
369 }
370 }
371
372 //healing
373 double fdheal = gammaheal * std::min(0.0, (m_strain_rates(i, j).eigen1 - healThreshold));
374 if (geometry.cell_type.icy(i, j)) {
375 if (constant_healing) {
376 fdheal = gammaheal * (-healThreshold);
377 if (fracture_weighted_healing) {
378 D_new(i, j) += fdheal * dt * (1 - D(i, j));
379 } else {
380 D_new(i, j) += fdheal * dt;
381 }
382 } else if (m_strain_rates(i, j).eigen1 < healThreshold) {
383 if (fracture_weighted_healing) {
384 D_new(i, j) += fdheal * dt * (1 - D(i, j));
385 } else {
386 D_new(i, j) += fdheal * dt;
387 }
388 }
389 }
390
391 // bounding
392 D_new(i, j) = pism::clip(D_new(i, j), 0.0, 1.0);
393
394 if (geometry.cell_type.icy(i, j)) {
395 //fracture toughness
396 m_toughness(i, j) = sigmat;
397
398 // fracture growth rate
399 if (sigmat > initThreshold) {
400 m_growth_rate(i, j) = fdnew;
401 //m_growth_rate(i,j)=gamma*(vPrinStrain1(i,j)-0.0)*(1-D_new(i,j));
402 } else {
403 m_growth_rate(i, j) = 0.0;
404 }
405
406 // fracture healing rate
407 if (geometry.cell_type.icy(i, j)) {
408 if (constant_healing or (m_strain_rates(i, j).eigen1 < healThreshold)) {
409 if (fracture_weighted_healing) {
410 m_healing_rate(i, j) = fdheal * (1 - D(i, j));
411 } else {
412 m_healing_rate(i, j) = fdheal;
413 }
414 } else {
415 m_healing_rate(i, j) = 0.0;
416 }
417 }
418
419 // fracture age since fracturing occurred
420 {
421 auto a = A.star(i, j);
422 A_new(i, j) -= dt * u * (u < 0 ? a.e - a.c : a.c - a.w) / dx;
423 A_new(i, j) -= dt * v * (v < 0 ? a.n - a.c : a.c - a.s) / dy;
424 A_new(i, j) += dt;
425 if (sigmat > initThreshold) {
426 A_new(i, j) = 0.0;
427 }
428 }
429
430 // additional flow enhancement due to fracture softening
431 double softening = pow((1.0 - (1.0 - soft_residual) * D_new(i, j)), -glen_exponent);
432 if (geometry.cell_type.icy(i, j)) {
433 m_flow_enhancement(i, j) = 1.0 / pow(softening, 1 / glen_exponent);
434 } else {
435 m_flow_enhancement(i, j) = 1.0;
436 }
437 }
438
439 // boundary condition
440 if (geometry.cell_type.grounded(i, j) and not do_fracground) {
441
442 if (bc_mask(i, j) > 0.5) {
443 D_new(i, j) = fdBoundaryValue;
444
445 {
446 A_new(i, j) = 0.0;
447 m_growth_rate(i, j) = 0.0;
448 m_healing_rate(i, j) = 0.0;
449 m_flow_enhancement(i, j) = 1.0;
450 m_toughness(i, j) = 0.0;
451 }
452 }
453 }
454
455 // ice free regions and boundary of computational domain
456 if (geometry.cell_type.ice_free(i, j) or i == 0 or j == 0 or i == Mx - 1 or j == My - 1) {
457
458 D_new(i, j) = 0.0;
459
460 {
461 A_new(i, j) = 0.0;
462 m_growth_rate(i, j) = 0.0;
463 m_healing_rate(i, j) = 0.0;
464 m_flow_enhancement(i, j) = 1.0;
465 m_toughness(i, j) = 0.0;
466 }
467 }
468
469 if (constant_fd) { // no fd evolution
470 D_new(i, j) = D(i, j);
471 }
472 }
473
474 A.copy_from(A_new);
475 D.copy_from(D_new);
476}
477
479 return {{"fracture_density", Diagnostic::wrap(m_density)},
480 {"fracture_growth_rate", Diagnostic::wrap(m_growth_rate)},
481 {"fracture_healing_rate", Diagnostic::wrap(m_healing_rate)},
482 {"fracture_flow_enhancement", Diagnostic::wrap(m_flow_enhancement)},
483 {"fracture_age", Diagnostic::wrap(m_age)},
484 {"fracture_toughness", Diagnostic::wrap(m_toughness)}
485 };
486}
487
489 return m_density;
490}
491
495
499
503
505 return m_age;
506}
507
509 return m_toughness;
510}
511
512} // end of namespace pism
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
A class defining a common interface for most PISM sub-models.
Definition Component.hh:118
static Ptr wrap(const T &input)
std::string name() const
Definition File.cc:274
High-level PISM I/O class.
Definition File.hh:55
void define_model_state_impl(const File &output) const
The default (empty implementation).
void restart(const File &input_file, int record)
const array::Scalar1 & density() const
void update(double dt, const Geometry &geometry, const array::Vector &velocity, const array::Scalar &hardness, const array::Scalar &inflow_boundary_mask)
FractureDensity(std::shared_ptr< const Grid > grid, std::shared_ptr< const rheology::FlowLaw > flow_law)
array::Array2D< stressbalance::DeviatoricStresses > m_deviatoric_stresses
components of horizontal stress tensor along axes and shear stress (temporary storage)
array::Scalar m_density_new
const array::Scalar & toughness() const
const array::Scalar & growth_rate() const
array::Array2D< stressbalance::PrincipalStrainRates > m_strain_rates
major and minor principal components of horizontal strain-rate tensor (temporary storage)
const array::Scalar & age() const
array::Scalar m_healing_rate
void bootstrap(const File &input_file)
void write_model_state_impl(const File &output) const
The default (empty implementation).
std::shared_ptr< const rheology::FlowLaw > m_flow_law
array::Scalar m_growth_rate
const array::Scalar & healing_rate() const
const array::Scalar & flow_enhancement() const
DiagnosticList diagnostics_impl() const
array::Scalar m_flow_enhancement
array::Vector1 m_velocity
Ghosted copy of the ice velocity.
array::CellType2 cell_type
Definition Geometry.hh:55
array::Scalar2 ice_thickness
Definition Geometry.hh:51
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
VariableMetadata & set_name(const std::string &name)
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
stencils::Star< T > star(int i, int j) const
Definition Array2D.hh:79
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
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 grounded(int i, int j) const
Definition CellType.hh:38
bool icy(int i, int j) const
Definition CellType.hh:42
@ PISM_DOUBLE
Definition IO_Flags.hh:52
void compute_2D_principal_strain_rates(const array::Vector1 &V, const array::CellType1 &mask, array::Array2D< PrincipalStrainRates > &result)
Compute eigenvalues of the horizontal, vertically-integrated strain rate tensor.
void compute_2D_stresses(const rheology::FlowLaw &flow_law, const array::Vector1 &velocity, const array::Scalar &hardness, const array::CellType1 &cell_type, array::Array2D< DeviatoricStresses > &result)
Compute 2D deviatoric stresses.
T clip(T x, T a, T b)
std::map< std::string, Diagnostic::Ptr > DiagnosticList