PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
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 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 
34 namespace pism {
35 namespace stressbalance {
36 
38  geometry = NULL;
39  new_bed_elevation = true;
40 
41  basal_melt_rate = NULL;
42  water_column_pressure = 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  */
60 void 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 
81  geometry->bed_elevation.write(output);
83 
84  geometry->ice_thickness.write(output);
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 
129  no_model_ice_thickness->write(output);
130  }
131 
134  }
135 }
136 
137 StressBalance::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 
158 }
159 
160 //! \brief Initialize the StressBalance object.
162  m_shallow_stress_balance->init();
163  m_modifier->init();
164 }
165 
166 //! \brief Performs the shallow stress balance computation.
167 void 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");
184  this->compute_volumetric_strain_heating(inputs);
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 
208  return m_cfl_2d;
209 }
210 
212  return m_cfl_3d;
213 }
214 
216  return m_shallow_stress_balance->velocity();
217 }
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 
244  return m_strain_heating;
245 }
246 
247 //! Compute vertical velocity using incompressibility of the ice.
248 /*!
249 The vertical velocity \f$w(x,y,z,t)\f$ is the velocity *relative to the
250 location of the base of the ice column*. That is, the vertical velocity
251 computed here is identified as \f$\tilde w(x,y,s,t)\f$ in the page
252 []@ref vertchange.
253 
254 Thus \f$w<0\f$ here means that that
255 that part of the ice is getting closer to the base of the ice, and so on.
256 The slope of the bed (i.e. relative to the geoid) and/or the motion of the
257 bed (i.e. from bed deformation) do not affect the vertical velocity.
258 
259 In fact the following statement is exactly true if the basal melt rate is zero:
260 the vertical velocity at a point in the ice is positive (negative) if and only
261 if the average horizontal divergence of the horizontal velocity, in the portion
262 of the ice column below that point, is negative (positive).
263 In particular, because \f$z=0\f$ is the location of the base of the ice
264 always, the only way to have \f$w(x,y,0,t) \ne 0\f$ is to have a basal melt
265 rate.
266 
267 Incompressibility itself says
268  \f[ \nabla\cdot\mathbf{U} + \frac{\partial w}{\partial z} = 0. \f]
269 This 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]
272 Here the value \f$w_b(x,y,t)\f$ is either zero or the negative of the basal melt rate
273 according to the value of the flag `geometry.update.use_basal_melt_rate`.
274 
275 The 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  */
440 static 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 
642 std::string StressBalance::stdout_report() const {
643  return m_shallow_stress_balance->stdout_report() + m_modifier->stdout_report();
644 }
645 
647  return m_shallow_stress_balance.get();
648 }
649 
651  return m_modifier.get();
652 }
653 
654 
655 void StressBalance::define_model_state_impl(const File &output) const {
656  m_shallow_stress_balance->define_model_state(output);
657  m_modifier->define_model_state(output);
658 }
659 
660 void StressBalance::write_model_state_impl(const File &output) const {
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 /*!
667 Calculates all components \f$D_{xx}, D_{yy}, D_{xy}=D_{yx}\f$ of the
668 vertically-averaged strain rate tensor \f$D\f$ [\ref SchoofStream]. Then computes
669 the eigenvalues `result(i,j,0)` = (maximum eigenvalue), `result(i,j,1)` = (minimum
670 eigenvalue). Uses the provided thickness to make decisions (PIK) about computing
671 strain rates near calving front.
672 
673 Note that `result(i,j,0)` >= `result(i,j,1)`, but there is no necessary relation between
674 the magnitudes, and either principal strain rate could be negative or positive.
675 
676 Result can be used in a calving law, for example in eigencalving (PIK).
677 
678 Note: strain rates will be derived from SSA velocities, using ghosts when
679 necessary. Both implementations (SSAFD and SSAFEM) call
680 update_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
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:56
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 & output_units(const std::string &input)
VariableMetadata & 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:65
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:120
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:800
std::shared_ptr< const Grid > grid() const
Definition: Array.cc:132
void add(double alpha, const Array &x)
Result: v <- v + alpha * x. Calls VecAXPY.
Definition: Array.cc:235
SpatialVariableMetadata & metadata(unsigned int N=0)
Returns a reference to the SpatialVariableMetadata object containing metadata for the compoment N.
Definition: Array.cc:553
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:35
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::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 * fracture_density
const array::Scalar * basal_melt_rate
const array::Vector * bc_values
Shallow stress balance modifier (such as the non-sliding SIA).
Definition: SSB_Modifier.hh:39
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
@ WITHOUT_GHOSTS
Definition: Array.hh:62
void append_time(const File &file, const Config &config, double time_seconds)
Prepare a file for output.
Definition: io_helpers.cc:247
@ PISM_READWRITE_MOVE
create a file for writing, move foo.nc to foo.nc~ if present
Definition: IO_Flags.hh:78
void define_time(const File &file, const Context &ctx)
Prepare a file for output.
Definition: io_helpers.cc:213
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:57
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.
Definition: timestepping.cc:44