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