Loading [MathJax]/extensions/tex2jax.js
PISM, A Parallel Ice Sheet Model 2.2.2-d6b3a29ca committed by Constantine Khrulev on 2025-03-28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
StressBalance.cc
Go to the documentation of this file.
1// Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024 Constantine Khroulev and Ed Bueler
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/StressBalance.hh"
20#include "pism/stressbalance/ShallowStressBalance.hh"
21#include "pism/stressbalance/SSB_Modifier.hh"
22#include "pism/util/EnthalpyConverter.hh"
23#include "pism/rheology/FlowLaw.hh"
24#include "pism/util/Grid.hh"
25#include "pism/util/Mask.hh"
26#include "pism/util/ConfigInterface.hh"
27#include "pism/util/error_handling.hh"
28#include "pism/util/Profiling.hh"
29#include "pism/util/array/CellType.hh"
30#include "pism/util/Time.hh"
31#include "pism/geometry/Geometry.hh"
32#include "pism/util/Context.hh"
33
34namespace pism {
35namespace stressbalance {
36
38 geometry = NULL;
39 new_bed_elevation = true;
40
41 basal_melt_rate = NULL;
43 fracture_density = NULL;
44 basal_yield_stress = NULL;
45
46 enthalpy = NULL;
47 age = NULL;
48
49 bc_mask = NULL;
50 bc_values = NULL;
51
52 no_model_mask = NULL;
55}
56
57/*!
58 * Save stress balance inputs to a file (for debugging).
59 */
60void Inputs::dump(const char *filename) const {
61 if (not geometry) {
62 return;
63 }
64
65 auto ctx = geometry->ice_thickness.grid()->ctx();
66 auto config = ctx->config();
67
68 File output(ctx->com(), filename,
69 string_to_backend(config->get_string("output.format")),
71
72 config->write(output);
73
74 io::define_time(output, *ctx);
75 io::append_time(output, config->get_string("time.dimension_name"), ctx->time()->current());
76
77 {
78 geometry->latitude.write(output);
79 geometry->longitude.write(output);
80
83
86
87 geometry->cell_type.write(output);
90 }
91
92 if (basal_melt_rate) {
93 basal_melt_rate->write(output);
94 }
95
98 }
99
100 if (fracture_density) {
101 fracture_density->write(output);
102 }
103
104 if (basal_yield_stress) {
105 basal_yield_stress->write(output);
106 }
107
108 if (enthalpy) {
109 enthalpy->write(output);
110 }
111
112 if (age) {
113 age->write(output);
114 }
115
116 if (bc_mask) {
117 bc_mask->write(output);
118 }
119
120 if (bc_values) {
121 bc_values->write(output);
122 }
123
124 if (no_model_mask) {
125 no_model_mask->write(output);
126 }
127
130 }
131
134 }
135}
136
137StressBalance::StressBalance(std::shared_ptr<const Grid> g,
138 std::shared_ptr<ShallowStressBalance> sb,
139 std::shared_ptr<SSB_Modifier> ssb_mod)
140 : Component(g),
141 m_w(m_grid, "wvel_rel", array::WITHOUT_GHOSTS, m_grid->z()),
142 m_strain_heating(m_grid, "strain_heating", array::WITHOUT_GHOSTS, m_grid->z()),
143 m_shallow_stress_balance(sb),
144 m_modifier(ssb_mod) {
145
146 m_w.metadata(0)
147 .long_name("vertical velocity of ice, relative to base of ice directly below")
148 .units("m s^-1")
149 .output_units("m year^-1")
150 .set_time_independent(false);
151
153 .long_name("rate of strain heating in ice (dissipation heating)")
154 .units("W m^-3");
155}
156
159
160//! \brief Initialize the StressBalance object.
163 m_modifier->init();
164}
165
166//! \brief Performs the shallow stress balance computation.
167void StressBalance::update(const Inputs &inputs, bool full_update) {
168
169 try {
170 profiling().begin("stress_balance.shallow");
171 m_shallow_stress_balance->update(inputs, full_update);
172 profiling().end("stress_balance.shallow");
173
174 profiling().begin("stress_balance.modifier");
175 m_modifier->update(m_shallow_stress_balance->velocity(),
176 inputs, full_update);
177 profiling().end("stress_balance.modifier");
178
179 if (full_update) {
180 const array::Array3D &u = m_modifier->velocity_u();
181 const array::Array3D &v = m_modifier->velocity_v();
182
183 profiling().begin("stress_balance.strain_heat");
185 profiling().end("stress_balance.strain_heat");
186
187 profiling().begin("stress_balance.vertical_velocity");
189 u, v, inputs.basal_melt_rate, m_w);
190 profiling().end("stress_balance.vertical_velocity");
191
193 inputs.geometry->cell_type,
194 u, v, m_w);
195 }
196
198 inputs.geometry->cell_type,
199 m_shallow_stress_balance->velocity());
200 }
201 catch (RuntimeError &e) {
202 e.add_context("updating the stress balance");
203 throw;
204 }
205}
206
210
214
218
220 return m_modifier->diffusive_flux();
221}
222
224 return m_modifier->max_diffusivity();
225}
226
228 return m_modifier->velocity_u();
229}
230
232 return m_modifier->velocity_v();
233}
234
236 return m_w;
237}
238
240 return m_shallow_stress_balance->basal_frictional_heating();
241}
242
246
247//! Compute vertical velocity using incompressibility of the ice.
248/*!
249The vertical velocity \f$w(x,y,z,t)\f$ is the velocity *relative to the
250location of the base of the ice column*. That is, the vertical velocity
251computed here is identified as \f$\tilde w(x,y,s,t)\f$ in the page
252[]@ref vertchange.
253
254Thus \f$w<0\f$ here means that that
255that part of the ice is getting closer to the base of the ice, and so on.
256The slope of the bed (i.e. relative to the geoid) and/or the motion of the
257bed (i.e. from bed deformation) do not affect the vertical velocity.
258
259In fact the following statement is exactly true if the basal melt rate is zero:
260the vertical velocity at a point in the ice is positive (negative) if and only
261if the average horizontal divergence of the horizontal velocity, in the portion
262of the ice column below that point, is negative (positive).
263In particular, because \f$z=0\f$ is the location of the base of the ice
264always, the only way to have \f$w(x,y,0,t) \ne 0\f$ is to have a basal melt
265rate.
266
267Incompressibility itself says
268 \f[ \nabla\cdot\mathbf{U} + \frac{\partial w}{\partial z} = 0. \f]
269This is immediately equivalent to the integral
270 \f[ w(x,y,z,t) = - \int_{b(x,y,t)}^{z} \nabla\cdot\mathbf{U}\,d\zeta
271 + w_b(x,y,t). \f]
272Here the value \f$w_b(x,y,t)\f$ is either zero or the negative of the basal melt rate
273according to the value of the flag `geometry.update.use_basal_melt_rate`.
274
275The vertical integral is computed by the trapezoid rule.
276 */
278 const array::Array3D &u,
279 const array::Array3D &v,
280 const array::Scalar *basal_melt_rate,
281 array::Array3D &result) {
282
283 const bool use_upstream_fd = m_config->get_string("stress_balance.vertical_velocity_approximation") == "upstream";
284
285 array::AccessScope list{&u, &v, &mask, &result};
286
287 if (basal_melt_rate) {
288 list.add(*basal_melt_rate);
289 }
290
291 const std::vector<double> &z = m_grid->z();
292 const unsigned int Mz = m_grid->Mz();
293
294 const double
295 dx = m_grid->dx(),
296 dy = m_grid->dy();
297
298 std::vector<double> u_x_plus_v_y(Mz);
299
300 for (auto p = m_grid->points(); p; p.next()) {
301 const int i = p.i(), j = p.j();
302
303 double *w_ij = result.get_column(i,j);
304
305 const double
306 *u_w = u.get_column(i-1,j),
307 *u_ij = u.get_column(i,j),
308 *u_e = u.get_column(i+1,j);
309 const double
310 *v_s = v.get_column(i,j-1),
311 *v_ij = v.get_column(i,j),
312 *v_n = v.get_column(i,j+1);
313
314 double
315 west = 1.0,
316 east = 1.0,
317 south = 1.0,
318 north = 1.0;
319 double
320 D_x = 0, // 1/(dx), 1/(2dx), or 0
321 D_y = 0; // 1/(dy), 1/(2dy), or 0
322
323 // Switch between second-order centered differences in the interior and
324 // first-order one-sided differences at ice margins.
325
326 // x-derivative
327 {
328 // use basal velocity to determine FD direction ("upwind" when it's clear, centered when it's
329 // not)
330 if (use_upstream_fd) {
331 const double
332 uw = 0.5 * (u_w[0] + u_ij[0]),
333 ue = 0.5 * (u_ij[0] + u_e[0]);
334
335 if (uw > 0.0 and ue >= 0.0) {
336 west = 1.0;
337 east = 0.0;
338 } else if (uw <= 0.0 and ue < 0.0) {
339 west = 0.0;
340 east = 1.0;
341 } else {
342 west = 1.0;
343 east = 1.0;
344 }
345 }
346
347 if ((mask.icy(i,j) and mask.ice_free(i+1,j)) or (mask.ice_free(i,j) and mask.icy(i+1,j))) {
348 east = 0;
349 }
350 if ((mask.icy(i,j) and mask.ice_free(i-1,j)) or (mask.ice_free(i,j) and mask.icy(i-1,j))) {
351 west = 0;
352 }
353
354 if (east + west > 0) {
355 D_x = 1.0 / (dx * (east + west));
356 } else {
357 D_x = 0.0;
358 }
359 }
360
361 // y-derivative
362 {
363 // use basal velocity to determine FD direction ("upwind" when it's clear, centered when it's
364 // not)
365 if (use_upstream_fd) {
366 const double
367 vs = 0.5 * (v_s[0] + v_ij[0]),
368 vn = 0.5 * (v_ij[0] + v_n[0]);
369
370 if (vs > 0.0 and vn >= 0.0) {
371 south = 1.0;
372 north = 0.0;
373 } else if (vs <= 0.0 and vn < 0.0) {
374 south = 0.0;
375 north = 1.0;
376 } else {
377 south = 1.0;
378 north = 1.0;
379 }
380 }
381
382 if ((mask.icy(i,j) and mask.ice_free(i,j+1)) or (mask.ice_free(i,j) and mask.icy(i,j+1))) {
383 north = 0;
384 }
385 if ((mask.icy(i,j) and mask.ice_free(i,j-1)) or (mask.ice_free(i,j) and mask.icy(i,j-1))) {
386 south = 0;
387 }
388
389 if (north + south > 0) {
390 D_y = 1.0 / (dy * (north + south));
391 } else {
392 D_y = 0.0;
393 }
394 }
395
396 // compute u_x + v_y using a vectorizable loop
397 for (unsigned int k = 0; k < Mz; ++k) {
398 double
399 u_x = D_x * (west * (u_ij[k] - u_w[k]) + east * (u_e[k] - u_ij[k])),
400 v_y = D_y * (south * (v_ij[k] - v_s[k]) + north * (v_n[k] - v_ij[k]));
401 u_x_plus_v_y[k] = u_x + v_y;
402 }
403
404 // at the base: include the basal melt rate
405 if (basal_melt_rate != NULL) {
406 w_ij[0] = - (*basal_melt_rate)(i,j);
407 } else {
408 w_ij[0] = 0.0;
409 }
410
411 // within the ice and above:
412 for (unsigned int k = 1; k < Mz; ++k) {
413 const double dz = z[k] - z[k-1];
414
415 w_ij[k] = w_ij[k - 1] - (0.5 * dz) * (u_x_plus_v_y[k] + u_x_plus_v_y[k - 1]);
416 }
417 }
418}
419
420/**
421 * This function computes \f$D^2\f$ defined by
422 *
423 * \f[ 2D^2 = D_{ij} D_{ij}\f]
424 * or
425 * \f[
426 * D^2 = \frac{1}{2}\,\left(\frac{1}{2}\,(v_{z})^2 + (v_{y} + u_{x})^2 +
427 * (v_{y})^2 + \frac{1}{2}\,(v_{x} + u_{y})^2 + \frac{1}{2}\,(u_{z})^2 +
428 * (u_{x})^2\right)
429 * \f]
430 *
431 * (note the use of the summation convention). Here \f$D_{ij}\f$ is the
432 * strain rate tensor. See
433 * StressBalance::compute_volumetric_strain_heating() for details.
434 *
435 * @param u_x,u_y,u_z partial derivatives of \f$u\f$, the x-component of the ice velocity
436 * @param v_x,v_y,v_z partial derivatives of \f$v\f$, the y-component of the ice velocity
437 *
438 * @return \f$D^2\f$, where \f$D\f$ is defined above.
439 */
440static inline double D2(double u_x, double u_y, double u_z, double v_x, double v_y, double v_z) {
441 return 0.5 * (PetscSqr(u_x + v_y) + u_x*u_x + v_y*v_y + 0.5 * (PetscSqr(u_y + v_x) + u_z*u_z + v_z*v_z));
442}
443
444/**
445 \brief Computes the volumetric strain heating using horizontal
446 velocity.
447
448 Following the notation used in [\ref BBssasliding], let \f$u\f$ be a
449 three-dimensional *vector* velocity field. Then the strain rate
450 tensor \f$D_{ij}\f$ is defined by
451
452 \f[ D_{ij} = \frac 12 \left(\diff{u_{i}}{x_{j}} + \diff{u_{j}}{x_{i}} \right), \f]
453
454 Where \f$i\f$ and \f$j\f$ range from \f$1\f$ to \f$3\f$.
455
456 The flow law in the viscosity form states
457
458 \f[ \tau_{ij} = 2 \eta D_{ij}, \f]
459
460 and the nonlinear ice viscosity satisfies
461
462 \f[ 2 \eta = B(T) D^{(1/n) - 1}. \f]
463
464 Here \f$D^{2}\f$ is defined by \f$2D^{2} = D_{ij}D_{ij}\f$ (using the
465 summation convention) and \f$B(T) = A(T)^{-1/n}\f$ is the ice hardness.
466
467 Now the volumetric strain heating is
468
469 \f[ \Sigma = \sum_{i,j=1}^{3}D_{ij}\tau_{ij} = 2 B(T) D^{(1/n) + 1}. \f]
470
471 We use an *approximation* of \f$D_{ij}\f$ common in shallow ice models:
472
473 - we assume that horizontal derivatives of the vertical velocity are
474 much smaller than \f$z\f$ derivatives horizontal velocity
475 components \f$u\f$ and \f$v\f$. (We drop \f$w_x\f$ and \f$w_y\f$
476 terms in \f$D_{ij}\f$.)
477
478 - we use the incompressibility of ice to approximate \f$w_z\f$:
479
480 \f[ w_z = - (u_x + v_y). \f]
481
482 Requires ghosts of `u` and `v` velocity components and uses the fact
483 that `u` and `v` above the ice are filled using constant
484 extrapolation.
485
486 Resulting field does not have ghosts.
487
488 Below is the *Maxima* code that produces the expression evaluated by D2().
489
490 derivabbrev : true;
491 U : [u, v, w]; X : [x, y, z]; depends(U, X);
492 gradef(w, x, 0); gradef(w, y, 0);
493 gradef(w, z, -(diff(u, x) + diff(v, y)));
494 d[i,j] := 1/2 * (diff(U[i], X[j]) + diff(U[j], X[i]));
495 D : genmatrix(d, 3, 3), ratsimp, factor;
496 tex('D = D);
497 tex('D^2 = 1/2 * mat_trace(D . D));
498
499 @return 0 on success
500 */
502 PetscErrorCode ierr;
503
504 const rheology::FlowLaw &flow_law = *m_shallow_stress_balance->flow_law();
505 EnthalpyConverter::Ptr EC = m_shallow_stress_balance->enthalpy_converter();
506
507 const array::Array3D
508 &u = m_modifier->velocity_u(),
509 &v = m_modifier->velocity_v();
510
511 const array::Scalar &thickness = inputs.geometry->ice_thickness;
512 const array::Array3D *enthalpy = inputs.enthalpy;
513
514 const auto &mask = inputs.geometry->cell_type;
515
516 double
517 enhancement_factor = m_shallow_stress_balance->flow_enhancement_factor(),
518 n = flow_law.exponent(),
519 exponent = 0.5 * (1.0 / n + 1.0),
520 e_to_a_power = pow(enhancement_factor,-1.0/n);
521
522 array::AccessScope list{&mask, enthalpy, &m_strain_heating, &thickness, &u, &v};
523
524 const std::vector<double> &z = m_grid->z();
525 const unsigned int Mz = m_grid->Mz();
526 std::vector<double> depth(Mz), pressure(Mz), hardness(Mz);
527
528 ParallelSection loop(m_grid->com);
529 try {
530 for (auto p = m_grid->points(); p; p.next()) {
531 const int i = p.i(), j = p.j();
532
533 double H = thickness(i, j);
534 int ks = m_grid->kBelowHeight(H);
535 const double
536 *u_ij, *u_w, *u_n, *u_e, *u_s,
537 *v_ij, *v_w, *v_n, *v_e, *v_s;
538 double *Sigma;
539 const double *E_ij;
540
541 double west = 1, east = 1, south = 1, north = 1,
542 D_x = 0, // 1/(dx), 1/(2dx), or 0
543 D_y = 0; // 1/(dy), 1/(2dy), or 0
544
545 // x-derivative
546 {
547 if ((mask.icy(i,j) and mask.ice_free(i+1,j)) or (mask.ice_free(i,j) and mask.icy(i+1,j))) {
548 east = 0;
549 }
550 if ((mask.icy(i,j) and mask.ice_free(i-1,j)) or (mask.ice_free(i,j) and mask.icy(i-1,j))) {
551 west = 0;
552 }
553
554 if (east + west > 0) {
555 D_x = 1.0 / (m_grid->dx() * (east + west));
556 } else {
557 D_x = 0.0;
558 }
559 }
560
561 // y-derivative
562 {
563 if ((mask.icy(i,j) and mask.ice_free(i,j+1)) or (mask.ice_free(i,j) and mask.icy(i,j+1))) {
564 north = 0;
565 }
566 if ((mask.icy(i,j) and mask.ice_free(i,j-1)) or (mask.ice_free(i,j) and mask.icy(i,j-1))) {
567 south = 0;
568 }
569
570 if (north + south > 0) {
571 D_y = 1.0 / (m_grid->dy() * (north + south));
572 } else {
573 D_y = 0.0;
574 }
575 }
576
577 u_ij = u.get_column(i, j);
578 u_w = u.get_column(i - 1, j);
579 u_e = u.get_column(i + 1, j);
580 u_s = u.get_column(i, j - 1);
581 u_n = u.get_column(i, j + 1);
582
583 v_ij = v.get_column(i, j);
584 v_w = v.get_column(i - 1, j);
585 v_e = v.get_column(i + 1, j);
586 v_s = v.get_column(i, j - 1);
587 v_n = v.get_column(i, j + 1);
588
589 E_ij = enthalpy->get_column(i, j);
590 Sigma = m_strain_heating.get_column(i, j);
591
592 for (int k = 0; k <= ks; ++k) {
593 depth[k] = H - z[k];
594 }
595
596 // pressure added by the ice (i.e. pressure difference between the
597 // current level and the top of the column)
598 EC->pressure(depth, ks, pressure); // FIXME issue #15
599
600 flow_law.hardness_n(E_ij, pressure.data(), ks + 1, hardness.data());
601
602 for (int k = 0; k <= ks; ++k) {
603 double dz;
604
605 double u_z = 0.0, v_z = 0.0,
606 u_x = D_x * (west * (u_ij[k] - u_w[k]) + east * (u_e[k] - u_ij[k])),
607 u_y = D_y * (south * (u_ij[k] - u_s[k]) + north * (u_n[k] - u_ij[k])),
608 v_x = D_x * (west * (v_ij[k] - v_w[k]) + east * (v_e[k] - v_ij[k])),
609 v_y = D_y * (south * (v_ij[k] - v_s[k]) + north * (v_n[k] - v_ij[k]));
610
611 if (k > 0) {
612 dz = z[k+1] - z[k-1];
613 u_z = (u_ij[k+1] - u_ij[k-1]) / dz;
614 v_z = (v_ij[k+1] - v_ij[k-1]) / dz;
615 } else {
616 // use one-sided differences for u_z and v_z on the bottom level
617 dz = z[1] - z[0];
618 u_z = (u_ij[1] - u_ij[0]) / dz;
619 v_z = (v_ij[1] - v_ij[0]) / dz;
620 }
621
622 Sigma[k] = 2.0 * e_to_a_power * hardness[k] * pow(D2(u_x, u_y, u_z, v_x, v_y, v_z), exponent);
623 } // k-loop
624
625 int remaining_levels = Mz - (ks + 1);
626 if (remaining_levels > 0) {
627#if PETSC_VERSION_LT(3, 12, 0)
628 ierr = PetscMemzero(&Sigma[ks+1],
629 remaining_levels*sizeof(double));
630#else
631 ierr = PetscArrayzero(&Sigma[ks+1], remaining_levels);
632#endif
633 PISM_CHK(ierr, "PetscMemzero");
634 }
635 }
636 } catch (...) {
637 loop.failed();
638 }
639 loop.check();
640}
641
642std::string StressBalance::stdout_report() const {
643 return m_shallow_stress_balance->stdout_report() + m_modifier->stdout_report();
644}
645
649
651 return m_modifier.get();
652}
653
654
657 m_modifier->define_model_state(output);
658}
659
661 m_shallow_stress_balance->write_model_state(output);
662 m_modifier->write_model_state(output);
663}
664
665//! \brief Compute eigenvalues of the horizontal, vertically-integrated strain rate tensor.
666/*!
667Calculates all components \f$D_{xx}, D_{yy}, D_{xy}=D_{yx}\f$ of the
668vertically-averaged strain rate tensor \f$D\f$ [\ref SchoofStream]. Then computes
669the eigenvalues `result(i,j,0)` = (maximum eigenvalue), `result(i,j,1)` = (minimum
670eigenvalue). Uses the provided thickness to make decisions (PIK) about computing
671strain rates near calving front.
672
673Note that `result(i,j,0)` >= `result(i,j,1)`, but there is no necessary relation between
674the magnitudes, and either principal strain rate could be negative or positive.
675
676Result can be used in a calving law, for example in eigencalving (PIK).
677
678Note: strain rates will be derived from SSA velocities, using ghosts when
679necessary. Both implementations (SSAFD and SSAFEM) call
680update_ghosts() to ensure that ghost values are up to date.
681 */
683 const array::CellType1 &mask,
685
686 using mask::ice_free;
687
688 auto grid = result.grid();
689 double dx = grid->dx();
690 double dy = grid->dy();
691
692 array::AccessScope list{&V, &mask, &result};
693
694 for (auto p = grid->points(); p; p.next()) {
695 const int i = p.i(), j = p.j();
696
697 if (mask.ice_free(i,j)) {
698 result(i, j).eigen1 = 0.0;
699 result(i, j).eigen2 = 0.0;
700 continue;
701 }
702
703 auto m = mask.star(i,j);
704 auto U = V.star(i,j);
705
706 // strain in units s^-1
707 double u_x = 0, u_y = 0, v_x = 0, v_y = 0,
708 east = 1, west = 1, south = 1, north = 1;
709
710 // Computes u_x using second-order centered finite differences written as
711 // weighted sums of first-order one-sided finite differences.
712 //
713 // Given the cell layout
714 // *----n----*
715 // | |
716 // | |
717 // w e
718 // | |
719 // | |
720 // *----s----*
721 // east == 0 if the east neighbor of the current cell is ice-free. In
722 // this case we use the left- (west-) sided difference.
723 //
724 // If both neighbors in the east-west (x) direction are ice-free the
725 // x-derivative is set to zero (see u_x, v_x initialization above).
726 //
727 // Similarly in other directions.
728 if (ice_free(m.e)) {
729 east = 0;
730 }
731 if (ice_free(m.w)) {
732 west = 0;
733 }
734 if (ice_free(m.n)) {
735 north = 0;
736 }
737 if (ice_free(m.s)) {
738 south = 0;
739 }
740
741 if (west + east > 0) {
742 u_x = 1.0 / (dx * (west + east)) * (west * (U.c.u - U[West].u) + east * (U[East].u - U.c.u));
743 v_x = 1.0 / (dx * (west + east)) * (west * (U.c.v - U[West].v) + east * (U[East].v - U.c.v));
744 }
745
746 if (south + north > 0) {
747 u_y = 1.0 / (dy * (south + north)) * (south * (U.c.u - U[South].u) + north * (U[North].u - U.c.u));
748 v_y = 1.0 / (dy * (south + north)) * (south * (U.c.v - U[South].v) + north * (U[North].v - U.c.v));
749 }
750
751 const double A = 0.5 * (u_x + v_y), // A = (1/2) trace(D)
752 B = 0.5 * (u_x - v_y),
753 Dxy = 0.5 * (v_x + u_y), // B^2 = A^2 - u_x v_y
754 q = sqrt(B*B + Dxy*Dxy);
755 result(i, j).eigen1 = A + q;
756 result(i, j).eigen2 = A - q; // q >= 0 so e1 >= e2
757
758 }
759}
760
761//! @brief Compute 2D deviatoric stresses.
763 const array::Vector1 &velocity,
764 const array::Scalar &hardness,
765 const array::CellType1 &cell_type,
767
768 using mask::ice_free;
769
770 auto grid = result.grid();
771
772 const double
773 dx = grid->dx(),
774 dy = grid->dy();
775
776 array::AccessScope list{&velocity, &hardness, &result, &cell_type};
777
778 for (auto p = grid->points(); p; p.next()) {
779 const int i = p.i(), j = p.j();
780
781 if (cell_type.ice_free(i, j)) {
782 result(i,j).xx = 0.0;
783 result(i,j).yy = 0.0;
784 result(i,j).xy = 0.0;
785 continue;
786 }
787
788 auto m = cell_type.star(i,j);
789 auto U = velocity.star(i,j);
790
791 // strain in units s^-1
792 double u_x = 0, u_y = 0, v_x = 0, v_y = 0,
793 east = 1, west = 1, south = 1, north = 1;
794
795 // Computes u_x using second-order centered finite differences written as
796 // weighted sums of first-order one-sided finite differences.
797 //
798 // Given the cell layout
799 // *----n----*
800 // | |
801 // | |
802 // w e
803 // | |
804 // | |
805 // *----s----*
806 // east == 0 if the east neighbor of the current cell is ice-free. In
807 // this case we use the left- (west-) sided difference.
808 //
809 // If both neighbors in the east-west (x) direction are ice-free the
810 // x-derivative is set to zero (see u_x, v_x initialization above).
811 //
812 // Similarly in y-direction.
813 if (ice_free(m.e)) {
814 east = 0;
815 }
816 if (ice_free(m.w)) {
817 west = 0;
818 }
819 if (ice_free(m.n)) {
820 north = 0;
821 }
822 if (ice_free(m.s)) {
823 south = 0;
824 }
825
826 if (west + east > 0) {
827 u_x = 1.0 / (dx * (west + east)) * (west * (U.c.u - U[West].u) + east * (U[East].u - U.c.u));
828 v_x = 1.0 / (dx * (west + east)) * (west * (U.c.v - U[West].v) + east * (U[East].v - U.c.v));
829 }
830
831 if (south + north > 0) {
832 u_y = 1.0 / (dy * (south + north)) * (south * (U.c.u - U[South].u) + north * (U[North].u - U.c.u));
833 v_y = 1.0 / (dy * (south + north)) * (south * (U.c.v - U[South].v) + north * (U[North].v - U.c.v));
834 }
835
836 double nu = 0.0;
837 flow_law.effective_viscosity(hardness(i, j),
838 secondInvariant_2D({u_x, v_x}, {u_y, v_y}),
839 &nu, NULL);
840
841 //get deviatoric stresses
842 result(i,j).xx = 2.0*nu*u_x;
843 result(i,j).yy = 2.0*nu*v_y;
844 result(i,j).xy = nu*(u_y+v_x);
845 }
846}
847
848
849} // end of namespace stressbalance
850} // end of namespace pism
const Config::ConstPtr m_config
configuration database used by this component
Definition Component.hh:158
const std::shared_ptr< const Grid > m_grid
grid used by this component
Definition Component.hh:156
void define_model_state(const File &output) const
Define model state variables in an output file.
Definition Component.cc:122
const Profiling & profiling() const
Definition Component.cc:113
A class defining a common interface for most PISM sub-models.
Definition Component.hh:118
std::shared_ptr< EnthalpyConverter > Ptr
High-level PISM I/O class.
Definition File.hh:55
array::Scalar1 sea_level_elevation
Definition Geometry.hh:48
array::Scalar cell_grounded_fraction
Definition Geometry.hh:56
array::Scalar2 ice_surface_elevation
Definition Geometry.hh:57
array::Scalar1 ice_area_specific_volume
Definition Geometry.hh:52
array::CellType2 cell_type
Definition Geometry.hh:55
array::Scalar2 ice_thickness
Definition Geometry.hh:51
array::Scalar longitude
Definition Geometry.hh:42
array::Scalar2 bed_elevation
Definition Geometry.hh:47
array::Scalar latitude
Definition Geometry.hh:41
void failed()
Indicates a failure of a parallel section.
void begin(const char *name) const
Definition Profiling.cc:75
void end(const char *name) const
Definition Profiling.cc:91
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
VariableMetadata & output_units(const std::string &input)
VariableMetadata & set_time_independent(bool flag)
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition Array.hh:64
stencils::Star< T > star(int i, int j) const
Definition Array2D.hh:79
A storage vector combining related fields in a struct.
Definition Array2D.hh:32
double * get_column(int i, int j)
Definition Array3D.cc:121
A virtual class collecting methods common to ice and bedrock 3D fields.
Definition Array3D.hh:33
void write(const std::string &filename) const
Definition Array.cc:722
std::shared_ptr< const Grid > grid() const
Definition Array.cc:131
void add(double alpha, const Array &x)
Result: v <- v + alpha * x. Calls VecAXPY.
Definition Array.cc:206
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 icy(int i, int j) const
Definition CellType.hh:42
A class for storing and accessing internal staggered-grid 2D fields. Uses dof=2 storage....
Definition Staggered.hh:37
void effective_viscosity(double hardness, double gamma, double *nu, double *dnu) const
Computes the regularized effective viscosity and its derivative with respect to the second invariant ...
Definition FlowLaw.cc:162
void hardness_n(const double *enthalpy, const double *pressure, unsigned int n, double *result) const
Definition FlowLaw.cc:121
double exponent() const
Definition FlowLaw.cc:74
const array::Scalar1 * fracture_density
const array::Scalar * basal_yield_stress
const array::Scalar * bc_mask
const array::Array3D * age
void dump(const char *filename) const
const array::Scalar * no_model_ice_thickness
const array::Scalar2 * no_model_surface_elevation
const array::Scalar * water_column_pressure
const array::Array3D * enthalpy
const array::Scalar2 * no_model_mask
const array::Scalar * basal_melt_rate
const array::Vector * bc_values
Shallow stress balance modifier (such as the non-sliding SIA).
Shallow stress balance (such as the SSA).
std::shared_ptr< SSB_Modifier > m_modifier
virtual void write_model_state_impl(const File &output) const
The default (empty implementation).
const array::Array3D & velocity_w() const
virtual void define_model_state_impl(const File &output) const
The default (empty implementation).
const SSB_Modifier * modifier() const
Returns a pointer to a stress balance modifier implementation.
const array::Array3D & velocity_u() const
Get components of the the 3D velocity field.
virtual void compute_volumetric_strain_heating(const Inputs &inputs)
Computes the volumetric strain heating using horizontal velocity.
std::shared_ptr< ShallowStressBalance > m_shallow_stress_balance
double max_diffusivity() const
Get the max diffusivity (for the adaptive time-stepping).
virtual void compute_vertical_velocity(const array::CellType1 &mask, const array::Array3D &u, const array::Array3D &v, const array::Scalar *bmr, array::Array3D &result)
Compute vertical velocity using incompressibility of the ice.
void init()
Initialize the StressBalance object.
const array::Vector & advective_velocity() const
Get the thickness-advective (SSA) 2D velocity.
const array::Staggered & diffusive_flux() const
Get the diffusive (SIA) vertically-averaged flux on the staggered grid.
const ShallowStressBalance * shallow() const
Returns a pointer to a shallow stress balance solver implementation.
std::string stdout_report() const
Produce a report string for the standard output.
void update(const Inputs &inputs, bool full_update)
Update all the fields if (full_update), only update diffusive flux and max. diffusivity otherwise.
const array::Scalar & basal_frictional_heating() const
Get the basal frictional heating.
const array::Array3D & velocity_v() const
const array::Array3D & volumetric_strain_heating() const
StressBalance(std::shared_ptr< const Grid > g, std::shared_ptr< ShallowStressBalance > sb, std::shared_ptr< SSB_Modifier > ssb_mod)
#define PISM_CHK(errcode, name)
#define n
Definition exactTestM.c:37
void append_time(const File &file, const Config &config, double time_seconds)
Prepare a file for output.
@ PISM_READWRITE_MOVE
create a file for writing, move foo.nc to foo.nc~ if present
Definition IO_Flags.hh:74
void define_time(const File &file, const Context &ctx)
Prepare a file for output.
bool ice_free(int M)
Ice-free cell (grounded or ocean).
Definition Mask.hh:58
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.
static double D2(double u_x, double u_y, double u_z, double v_x, double v_y, double v_z)
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.
io::Backend string_to_backend(const std::string &backend)
Definition File.cc:56
CFLData max_timestep_cfl_2d(const array::Scalar &ice_thickness, const array::CellType &cell_type, const array::Vector &velocity)
Compute the CFL constant associated to first-order upwinding for the sliding contribution to mass con...
static double secondInvariant_2D(const Vector2d &U_x, const Vector2d &U_y)
Definition FlowLaw.hh:45
static const double g
Definition exactTestP.cc:36
static const double k
Definition exactTestP.cc:42
@ North
Definition stencils.hh:24
@ East
Definition stencils.hh:24
@ South
Definition stencils.hh:24
@ West
Definition stencils.hh:24
CFLData max_timestep_cfl_3d(const array::Scalar &ice_thickness, const array::CellType &cell_type, const array::Array3D &u3, const array::Array3D &v3, const array::Array3D &w3)
Compute the maximum velocities for time-stepping and reporting to user.