PISM, A Parallel Ice Sheet Model  stable v2.1.1 committed by Constantine Khrulev on 2024-12-04 13:36:58 -0900
Blatter.cc
Go to the documentation of this file.
1 /* Copyright (C) 2020, 2021, 2022, 2023 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 <cassert> // assert
21 #include <cmath> // std::pow, std::fabs, std::log10
22 #include <algorithm> // std::max
23 #include <cstring> // memset
24 
25 #include "pism/stressbalance/blatter/Blatter.hh"
26 #include "pism/util/error_handling.hh"
27 #include "pism/util/Vector2d.hh"
28 
29 #include "pism/stressbalance/blatter/util/DataAccess.hh"
30 #include "pism/stressbalance/blatter/util/grid_hierarchy.hh"
31 #include "pism/util/node_types.hh"
32 
33 #include "pism/rheology/FlowLawFactory.hh"
34 
35 #include "pism/geometry/Geometry.hh"
36 #include "pism/stressbalance/StressBalance.hh"
37 #include "pism/util/array/Array3D.hh"
38 #include "pism/util/pism_options.hh"
39 #include "pism/util/pism_utilities.hh" // pism::printf()
40 
41 namespace pism {
42 namespace stressbalance {
43 
44 /*!
45  * Compute node type using domain thickness and the thickness threshold `min_thickness`.
46  *
47  * An element contains ice if ice thickness at all its nodes equal or exceeds the
48  * `min_thickness` threshold.
49  *
50  * A node is *interior* if all four elements it belongs to contain ice.
51  *
52  * A node is *exterior* if it belongs to zero icy elements.
53  *
54  * A node that is neither interior nor exterior is a *boundary* node.
55  */
56 void Blatter::compute_node_type(double min_thickness) {
57 
58  array::Scalar1 node_type(m_grid, "node_type");
59  node_type.set(0.0);
60 
61  DMDALocalInfo info;
62  int ierr = DMDAGetLocalInfo(m_da, &info); PISM_CHK(ierr, "DMDAGetLocalInfo");
63  info = grid_transpose(info);
64 
65  // Note that dx, dy, and quadrature don't matter here.
66  fem::Q1Element2 E(info, 1.0, 1.0, fem::Q1Quadrature1());
67 
69 
70  array::AccessScope l{&node_type, &m_parameters};
71 
72  // Loop over all the elements with at least one owned node and compute the number of icy
73  // elements each node belongs to.
74  for (int j = info.gys; j < info.gys + info.gym - 1; j++) {
75  for (int i = info.gxs; i < info.gxs + info.gxm - 1; i++) {
76  E.reset(i, j);
77 
78  E.nodal_values(m_parameters.array(), p);
79 
80  // An element is "interior" (contains ice) if all of its nodes have thickness above
81  // the threshold
82  bool interior = true;
83  for (int k = 0; k < fem::q1::n_chi; ++k) {
84  if (p[k].thickness < min_thickness) {
85  interior = false;
86  break;
87  }
88  }
89 
90  for (int k = 0; k < fem::q1::n_chi; ++k) {
91  int ii, jj;
92  E.local_to_global(k, ii, jj);
93  node_type(ii, jj) += static_cast<double>(interior);
94  }
95  }
96  }
97 
98  node_type.update_ghosts();
99 
100  // Loop over all the owned nodes and turn the number of "icy" elements this node belongs
101  // to into node type.
102  for (int j = info.ys; j < info.ys + info.ym; j++) {
103  for (int i = info.xs; i < info.xs + info.xm; i++) {
104 
105  switch ((int)node_type(i, j)) {
106  case 4:
107  m_parameters(i, j).node_type = NODE_INTERIOR;
108  break;
109  case 0:
110  m_parameters(i, j).node_type = NODE_EXTERIOR;
111  break;
112  default:
113  m_parameters(i, j).node_type = NODE_BOUNDARY;
114  }
115  }
116  }
117 }
118 
119 /*!
120  * Returns true if a node is in the Dirichlet part of the boundary, false otherwise.
121  *
122  * Used by verification tests.
123  */
124 bool Blatter::dirichlet_node(const DMDALocalInfo &info, const fem::Element3::GlobalIndex& I) {
125  (void) info;
126  (void) I;
127  return false;
128 }
129 
130 /*! Dirichlet BC
131 */
132 Vector2d Blatter::u_bc(double x, double y, double z) const {
133  (void) x;
134  (void) y;
135  (void) z;
136  return {0.0, 0.0};
137 }
138 
139 /*!
140  * Return true if an element does not contain ice, i.e. is a part of the "exterior" of the
141  * ice mass.
142  *
143  * @param[in] node_type node type at the nodes of an element (an array of 8 integers; only
144  * 4 are used)
145  */
146 bool Blatter::exterior_element(const int *node_type) {
147  // number of nodes per map-plane cell
148  int N = 4;
149  for (int n = 0; n < N; ++n) {
150  if (node_type[n] == NODE_EXTERIOR) {
151  return true;
152  }
153  }
154  return false;
155 }
156 
157 /*!
158  * Return true if the current map-plane cell contains the grounding line, false otherwise.
159  *
160  * This is used to determine whether to use more quadrature points to estimate integrals
161  * over the bottom face of the basal element.
162  *
163  * The code takes advantage of the ordering of element nodes: lower 4 first, then upper 4.
164  * This means that we can loop over the first 4 nodes and ignore the other 4.
165  */
166 bool Blatter::grounding_line(const double *F) {
167 
168  // number of nodes per map-plane cell
169  int N = 4;
170 
171  bool
172  grounded = false,
173  floating = false;
174 
175  for (int n = 0; n < N; ++n) {
176  if (F[n] <= 0.0) {
177  grounded = true;
178  } else {
179  floating = true;
180  }
181  }
182 
183  return grounded and floating;
184 }
185 
186 /*!
187  * Return true if the current vertical face is partially submerged.
188  *
189  * This is used to determine whether to use more quadrature points to estimate integrals
190  * over this face when computing lateral boundary conditions.
191  */
192 bool Blatter::partially_submerged_face(int face, const double *z, const double *sea_level) {
193  const auto *nodes = fem::q13d::incident_nodes[face];
194 
195  // number of nodes per face
196  int N = 4;
197 
198  bool
199  above = false,
200  below = false;
201 
202  for (int n = 0; n < N; ++n) {
203  auto k = nodes[n];
204  if (z[k] > sea_level[k]) {
205  above = true;
206  } else {
207  below = true;
208  }
209  }
210 
211  return above and below;
212 }
213 
214 /*!
215  * Return true if the current face is a part of the marine ice boundary (i.e. at a
216  * partially-submerged vertical cliff), false otherwise.
217  *
218  * A face is a part of the marine boundary if all four nodes are boundary nodes *and* at
219  * least one map-plane location has bottom elevation below sea level (floatation level).
220  *
221  * If a node is *both* a boundary and a Dirichlet node (this may happen), then we treat it
222  * as a boundary node here: element.add_contribution() will do the right thing in this
223  * case.
224  */
226  const int *node_type,
227  const double *ice_bottom,
228  const double *sea_level) {
229  const auto *nodes = fem::q13d::incident_nodes[face];
230 
231  // number of nodes per face
232  int N = 4;
233 
234  // exclude faces that contain at least one node that is not a part of the boundary
235  for (int n = 0; n < N; ++n) {
236  if (not (node_type[nodes[n]] == NODE_BOUNDARY)) {
237  return false;
238  }
239  }
240 
241  // This face is a part of the lateral boundary. Now we need to check if ice_bottom is
242  // below sea_level at one of the nodes of this face.
243  for (int n = 0; n < N; ++n) {
244  if (ice_bottom[nodes[n]] < sea_level[nodes[n]]) {
245  return true;
246  }
247  }
248  return false;
249 }
250 
251 /*!
252  * Allocate the Blatter-Pattyn stress balance solver
253  *
254  * @param[in] grid PISM's grid.
255  * @param[in] Mz number of vertical levels
256  * @param[in] coarsening_factor grid coarsening factor
257  */
258 Blatter::Blatter(std::shared_ptr<const Grid> grid, int Mz, int coarsening_factor)
259  : ShallowStressBalance(grid),
260  m_parameters(grid, "bp_input_parameters", array::WITH_GHOSTS),
261  m_face4(grid->dx(), grid->dy(), fem::Q1Quadrature4()), // 4-point Gaussian quadrature
262  m_face100(grid->dx(), grid->dy(), fem::Q1QuadratureN(10)) // 100-point quadrature for grounding lines
263 {
264 
265  assert(m_face4.n_pts() <= m_Nq);
266  assert(m_face100.n_pts() <= m_Nq);
267 
268  auto pism_da = grid->get_dm(1, 0);
269 
270  int ierr = setup(*pism_da, grid->periodicity(), Mz, coarsening_factor, "bp_");
271  if (ierr != 0) {
273  "Failed to allocate a Blatter solver instance");
274  }
275 
276  {
277  std::vector<double> sigma(Mz);
278  double dz = 1.0 / (Mz - 1.0);
279  for (int i = 0; i < Mz; ++i) {
280  sigma[i] = i * dz;
281  }
282  sigma.back() = 1.0;
283 
284  m_u_sigma = std::make_shared<array::Array3D>(grid, "uvel_sigma", array::WITHOUT_GHOSTS, sigma);
285  m_u_sigma->metadata(0)
286  .long_name("u velocity component on the sigma grid")
287  .units("m s-1");
288 
289  m_v_sigma = std::make_shared<array::Array3D>(grid, "vvel_sigma", array::WITHOUT_GHOSTS, sigma);
290  m_v_sigma->metadata(0)
291  .long_name("v velocity component on the sigma grid")
292  .units("m s-1");
293 
294  std::map<std::string,std::string> z_attrs =
295  {{"axis", "Z"},
296  {"long_name", "scaled Z-coordinate in the ice (z_base=0, z_surface=1)"},
297  {"units", "1"},
298  {"positive", "up"}};
299 
300  m_u_sigma->metadata(0).z().set_name("z_sigma");
301  m_v_sigma->metadata(0).z().set_name("z_sigma");
302 
303  for (const auto &z_attr : z_attrs) {
304  m_u_sigma->metadata(0).z().set_string(z_attr.first, z_attr.second);
305  m_v_sigma->metadata(0).z().set_string(z_attr.first, z_attr.second);
306  }
307 
308  }
309 
310  {
311  rheology::FlowLawFactory ice_factory("stress_balance.blatter.", m_config, m_EC);
312  ice_factory.remove(ICE_GOLDSBY_KOHLSTEDT);
313  m_flow_law = ice_factory.create();
314  }
315 
316  double g = m_config->get_number("constants.standard_gravity");
317 
318  m_rho_ice_g = m_config->get_number("constants.ice.density") * g;
319  m_rho_ocean_g = m_config->get_number("constants.sea_water.density") * g;
320 
321  m_eta_transform = m_config->get_flag("stress_balance.blatter.use_eta_transform");
322 
323  m_glen_exponent = m_flow_law->exponent();
324 
325  double E = m_config->get_number("stress_balance.blatter.enhancement_factor");
326  m_E_viscosity = std::pow(E, -1.0 / m_glen_exponent);
327 
328  double softness = m_config->get_number("flow_law.isothermal_Glen.ice_softness"),
329  hardness = std::pow(softness, -1.0 / m_glen_exponent);
330 
331  m_scaling = m_rho_ice_g * hardness;
332 }
333 
334 /*!
335  * Restrict 2D and 3D model parameters from a fine grid to a coarse grid.
336  *
337  * Re-compute node types from geometry.
338  *
339  * This hook is called every time SNES needs to update coarse-grid data.
340  *
341  * FIXME: parameters restricted by this hook do not change from one SNES iteration to the
342  * next, so we can return early after the first one.
343  */
344 static PetscErrorCode blatter_restriction_hook(DM fine,
345  Mat mrestrict, Vec rscale, Mat inject,
346  DM coarse, void *ctx) {
347  // Get rid of warnings about unused arguments
348  (void) mrestrict;
349  (void) rscale;
350  (void) inject;
351  (void) ctx;
352 
353  PetscErrorCode ierr = restrict_data(fine, coarse, "3D_DM"); CHKERRQ(ierr);
354 
355  return 0;
356 }
357 
358 static PetscErrorCode blatter_coarsening_hook(DM dm_fine, DM dm_coarse, void *ctx) {
359  PetscErrorCode ierr;
360 
361  int mg_levels = 1;
362  {
363  const char *prefix;
364  ierr = DMGetOptionsPrefix(dm_fine, &prefix); CHKERRQ(ierr);
365  auto option = pism::printf("-%spc_mg_levels", prefix);
366  mg_levels = options::Integer(option, "", 1);
367  }
368 
369  ierr = setup_level(dm_coarse, mg_levels); CHKERRQ(ierr);
370 
371  ierr = DMCoarsenHookAdd(dm_coarse, blatter_coarsening_hook, blatter_restriction_hook, ctx); CHKERRQ(ierr);
372 
373  // 3D
374  ierr = create_restriction(dm_fine, dm_coarse, "3D_DM"); CHKERRQ(ierr);
375 
376  return 0;
377 }
378 
379 /*!
380  * Allocates the 3D DM, the corresponding solution vector, and the SNES solver.
381  */
382 PetscErrorCode Blatter::setup(DM pism_da, grid::Periodicity periodicity, int Mz,
383  int coarsening_factor,
384  const std::string &prefix) {
385  MPI_Comm comm;
386  PetscErrorCode ierr = PetscObjectGetComm((PetscObject)pism_da, &comm); CHKERRQ(ierr);
387 
388  // FIXME: add the ability to add a prefix to the option prefix. We need this to be able
389  // to run more than one instance of PISM in parallel.
390  auto option = pism::printf("-%spc_mg_levels", prefix.c_str());
391  int mg_levels = options::Integer(option, "", 1);
392 
393  // Check compatibility of Mz, mg_levels, and the coarsening_factor and stop if they are
394  // not compatible.
395  //
396  // We assume that the user also set "-bp_pc_type mg".
397  {
398  int c = coarsening_factor;
399  int M = mg_levels;
400  int mz = Mz;
401  while (M > 1) {
402  // Note: integer division
403  if (((mz - 1) / c) * c != mz - 1) {
404  int N = std::pow(c, (int)mg_levels - 1);
405  auto message = pism::printf("Blatter stress balance solver: settings\n"
406  "stress_balance.blatter.Mz = %d,\n"
407  "stress_balance.blatter.coarsening_factor = %d,\n"
408  "and '%s %d' are not compatible.\n"
409  "To use N = %d multigrid levels with the coarsening factor C = %d\n"
410  "stress_balance.blatter.Mz has to be equal to A * C^(N - 1) + 1\n"
411  "for some positive integer A, e.g. %d, %d, %d, ...",
412  Mz, c, option.c_str(), mg_levels, mg_levels, c,
413  N + 1, 2*N + 1, 3*N + 1);
414  throw RuntimeError(PISM_ERROR_LOCATION, message);
415  }
416  mz = (mz - 1) / c + 1;
417  M -= 1;
418  }
419  }
420 
421  // DM
422  //
423  // Note: in the PISM's DA pism_da PETSc's and PISM's meaning of x and y are the same.
424  {
425  PetscInt Mx, My;
426  PetscInt mx, my;
427  PetscInt dims;
428 
429  ierr = DMDAGetInfo(pism_da,
430  &dims, // dimensions
431  &Mx, &My, NULL, // grid size
432  &mx, &my, NULL, // numbers of processors in each direction
433  NULL, // number of degrees of freedom
434  NULL, // stencil width
435  NULL, NULL, NULL, // types of ghost nodes at the boundary
436  NULL); // stencil type
437  CHKERRQ(ierr);
438 
439  assert(dims == 2);
440 
441  const PetscInt
442  *lx = NULL,
443  *ly = NULL;
444  ierr = DMDAGetOwnershipRanges(pism_da, &lx, &ly, NULL); CHKERRQ(ierr);
445 
446  PetscInt
447  mz = 1,
448  dof = 2,
449  stencil_width = 1;
450 
451  DMBoundaryType
452  bx = (periodicity & grid::X_PERIODIC) != 0 ? DM_BOUNDARY_PERIODIC : DM_BOUNDARY_NONE,
453  by = (periodicity & grid::Y_PERIODIC) != 0 ? DM_BOUNDARY_PERIODIC : DM_BOUNDARY_NONE,
454  bz = DM_BOUNDARY_NONE;
455 
456  ierr = DMDACreate3d(comm,
457  bz, bx, by, // STORAGE_ORDER
458  DMDA_STENCIL_BOX,
459  Mz, Mx, My, // STORAGE_ORDER
460  mz, mx, my, // STORAGE_ORDER
461  dof,
462  stencil_width,
463  NULL, lx, ly, // STORAGE_ORDER
464  m_da.rawptr()); CHKERRQ(ierr);
465 
466  ierr = DMSetOptionsPrefix(m_da, prefix.c_str()); CHKERRQ(ierr);
467 
468  // semi-coarsening: coarsen in the vertical direction only
469  ierr = DMDASetRefinementFactor(m_da, coarsening_factor, 1, 1); CHKERRQ(ierr); // STORAGE_ORDER
470 
471  ierr = DMSetFromOptions(m_da); CHKERRQ(ierr);
472 
473  ierr = DMSetUp(m_da); CHKERRQ(ierr);
474 
475  // set up 3D parameter storage
476  ierr = setup_level(m_da, mg_levels); CHKERRQ(ierr);
477 
478  // tell PETSc how to coarsen this grid and how to restrict data to a coarser grid
479  ierr = DMCoarsenHookAdd(m_da, blatter_coarsening_hook, blatter_restriction_hook, NULL);
480  CHKERRQ(ierr);
481  }
482 
483  // Vec
484  {
485  ierr = DMCreateGlobalVector(m_da, m_x.rawptr()); CHKERRQ(ierr);
486 
487  ierr = VecSetOptionsPrefix(m_x, prefix.c_str()); CHKERRQ(ierr);
488 
489  ierr = VecSetFromOptions(m_x); CHKERRQ(ierr);
490 
491  ierr = VecDuplicate(m_x, m_x_old.rawptr()); CHKERRQ(ierr);
492  }
493 
494  // SNES
495  {
496  ierr = SNESCreate(comm, m_snes.rawptr()); CHKERRQ(ierr);
497 
498  ierr = SNESSetOptionsPrefix(m_snes, prefix.c_str()); CHKERRQ(ierr);
499 
500  ierr = SNESSetDM(m_snes, m_da); CHKERRQ(ierr);
501 
502  ierr = DMDASNESSetFunctionLocal(m_da, INSERT_VALUES,
503 #if PETSC_VERSION_LT(3,21,0)
504  (DMDASNESFunction)function_callback,
505 #else
506  (DMDASNESFunctionFn*)function_callback,
507 #endif
508  this); CHKERRQ(ierr);
509 
510  ierr = DMDASNESSetJacobianLocal(m_da,
511 #if PETSC_VERSION_LT(3,21,0)
512  (DMDASNESJacobian)jacobian_callback,
513 #else
514  (DMDASNESJacobianFn*)jacobian_callback,
515 #endif
516  this); CHKERRQ(ierr);
517 
518  ierr = SNESSetFromOptions(m_snes); CHKERRQ(ierr);
519 
520 
521  PetscBool ksp_use_ew = PETSC_FALSE;
522  ierr = SNESKSPGetUseEW(m_snes, &ksp_use_ew); CHKERRQ(ierr);
523  m_ksp_use_ew = (ksp_use_ew != 0U);
524  }
525 
526  return 0;
527 }
528 
529 /*!
530  * Set 2D parameters on the finest grid.
531  */
532 void Blatter::init_2d_parameters(const Inputs &inputs) {
533 
534  double
535  ice_density = m_config->get_number("constants.ice.density"),
536  water_density = m_config->get_number("constants.sea_water.density"),
537  alpha = ice_density / water_density;
538 
539  const array::Scalar
540  &tauc = *inputs.basal_yield_stress,
541  &H = inputs.geometry->ice_thickness,
542  &b = inputs.geometry->bed_elevation,
543  &sea_level = inputs.geometry->sea_level_elevation;
544 
545  {
546  array::AccessScope list{&tauc, &H, &b, &sea_level, &m_parameters};
547 
548  for (auto p = m_grid->points(); p; p.next()) {
549  const int i = p.i(), j = p.j();
550 
551  double
552  b_grounded = b(i, j),
553  b_floating = sea_level(i, j) - alpha * H(i, j),
554  s_grounded = b(i, j) + H(i, j),
555  s_floating = sea_level(i, j) + (1.0 - alpha) * H(i, j);
556 
557  m_parameters(i, j).tauc = tauc(i, j);
558  m_parameters(i, j).thickness = H(i, j);
559  m_parameters(i, j).sea_level = sea_level(i, j);
560  m_parameters(i, j).bed = std::max(b_grounded, b_floating);
561  m_parameters(i, j).node_type = NODE_EXTERIOR;
562  m_parameters(i, j).floatation = s_floating - s_grounded;
563  }
564  }
565 
566  // update ghosts here: the call to compute_node_type() uses ghosts of ice thickness
567  m_parameters.update_ghosts();
568 
569  double H_min = m_config->get_number("stress_balance.ice_free_thickness_standard");
570  compute_node_type(H_min);
571 
572  // update ghosts of node types stored in m_parameters
573  m_parameters.update_ghosts();
574 }
575 
576 /*!
577  * Set 3D parameters on the finest grid.
578  */
579 void Blatter::init_ice_hardness(const Inputs &inputs, const petsc::DM &da) {
580 
581  const auto *enthalpy = inputs.enthalpy;
582  // PISM's vertical grid:
583  const auto &zlevels = enthalpy->levels();
584  auto Mz = zlevels.size();
585 
586  // solver's vertical grid:
587  int Mz_sigma = 0;
588  {
589  DMDALocalInfo info;
590  int ierr = DMDAGetLocalInfo(da, &info); PISM_CHK(ierr, "DMDAGetLocalInfo");
591  info = grid_transpose(info);
592  Mz_sigma = info.mz;
593  }
594 
595  const auto &ice_thickness = inputs.geometry->ice_thickness;
596  DataAccess<double***> hardness(da, 3, NOT_GHOSTED);
597 
598  array::AccessScope list{enthalpy, &ice_thickness};
599 
600  for (auto p = m_grid->points(); p; p.next()) {
601  const int i = p.i(), j = p.j();
602 
603  double H = ice_thickness(i, j);
604 
605  const double *E = enthalpy->get_column(i, j);
606 
607  for (int k = 0; k < Mz_sigma; ++k) {
608  double
609  z = grid_z(0.0, H, Mz_sigma, k),
610  depth = H - z,
611  pressure = m_EC->pressure(depth),
612  E_local = 0.0;
613 
614  auto k0 = m_grid->kBelowHeight(z);
615 
616  if (k0 + 1 < Mz) {
617  double lambda = (z - zlevels[k0]) / (zlevels[k0 + 1] - zlevels[k0]);
618 
619  E_local = (1.0 - lambda) * E[k0] + lambda * E[k0 + 1];
620  } else {
621  E_local = E[Mz - 1];
622  }
623 
624  hardness[j][i][k] = m_flow_law->hardness(E_local, pressure); // STORAGE_ORDER
625 
626  } // end of the loop over sigma levels
627  } // end of the loop over grid points
628 }
629 
630 /*!
631  * Get values of 2D parameters at element nodes.
632  *
633  * This method is re-implemented by derived classes that use periodic boundary conditions.
634  */
636  Parameters **P,
637  int i,
638  int j,
639  int *node_type,
640  double *bottom_elevation,
641  double *ice_thickness,
642  double *surface_elevation,
643  double *sea_level) const {
644 
645  for (int n = 0; n < fem::q13d::n_chi; ++n) {
646  auto I = element.local_to_global(i, j, 0, n);
647 
648  auto p = P[I.j][I.i];
649 
650  node_type[n] = static_cast<int>(p.node_type);
651  bottom_elevation[n] = p.bed;
652  ice_thickness[n] = p.thickness;
653 
654  if (surface_elevation != nullptr) {
655  surface_elevation[n] = p.bed + p.thickness;
656  }
657 
658  if (sea_level != nullptr) {
659  sea_level[n] = p.sea_level;
660  }
661  }
662 }
663 
665  m_log->message(2, "* Initializing the Blatter stress balance...\n");
666 
668 
669  if (opts.type == INIT_RESTART) {
670  File input_file(m_grid->com, opts.filename, io::PISM_GUESS, io::PISM_READONLY);
671  bool u_sigma_found = input_file.find_variable("uvel_sigma");
672  bool v_sigma_found = input_file.find_variable("vvel_sigma");
673  unsigned int start = input_file.nrecords() - 1;
674 
675  if (u_sigma_found and v_sigma_found) {
676  m_log->message(3, "Reading uvel_sigma and vvel_sigma...\n");
677 
678  m_u_sigma->read(input_file, start);
679  m_v_sigma->read(input_file, start);
680 
682  } else {
684  "uvel_sigma and vvel_sigma not found");
685  }
686  } else {
687  int ierr = VecSet(m_x, 0.0); PISM_CHK(ierr, "VecSet");
688  }
689 }
690 
691 void Blatter::define_model_state_impl(const File &output) const {
692  m_u_sigma->define(output, io::PISM_DOUBLE);
693  m_v_sigma->define(output, io::PISM_DOUBLE);
694 }
695 
696 void Blatter::write_model_state_impl(const File &output) const {
697  m_u_sigma->write(output);
698  m_v_sigma->write(output);
699 }
700 
702 
703  DMDALocalInfo info;
704  int ierr = DMDAGetLocalInfo(m_da, &info); PISM_CHK(ierr, "DMDAGetLocalInfo");
705  info = grid_transpose(info);
706 
707  fem::Q1Element2 E(info, 1.0, 1.0, fem::Q1Quadrature1());
708 
710 
711  double R_min = 1e16, R_max = 0.0, R_avg = 0.0;
712  double dxy = std::max(m_grid->dx(), m_grid->dy());
713  double n_cells = 0.0;
714 
716  for (int j = info.ys; j < info.ys + info.ym - 1; j++) {
717  for (int i = info.xs; i < info.xs + info.xm - 1; i++) {
718 
719  E.reset(i, j);
720 
721  E.nodal_values(m_parameters.array(), P);
722 
723  int node_type[4];
724  for (int k = 0; k < 4; ++k) {
725  node_type[k] = static_cast<int>(P[k].node_type);
726  }
727 
728  if (exterior_element(node_type)) {
729  continue;
730  }
731 
732  n_cells += 1.0;
733 
734  double dz_max = 0.0;
735  for (int k = 0; k < 4; ++k) {
736  dz_max = std::max(P[k].thickness / (info.mz - 1), dz_max);
737  }
738  double R = dz_max / dxy;
739 
740  R_min = std::min(R, R_min);
741  R_max = std::max(R, R_max);
742  R_avg += R;
743  }
744  }
745 
746  n_cells = GlobalSum(m_grid->com, n_cells);
747  R_avg = GlobalSum(m_grid->com, R_avg);
748  R_avg /= std::max(n_cells, 1.0);
749 
750  R_min = GlobalMin(m_grid->com, R_min);
751  R_max = GlobalMax(m_grid->com, R_max);
752 
753  m_log->message(2,
754  "Blatter solver: %d * (%d - 1) = %d active elements\n",
755  (int)n_cells, (int)info.mz, (int)(n_cells * (info.mz - 1)));
756 
757  if (n_cells > 0) {
758  m_log->message(2,
759  " Vertical spacing (m): min = %3.3f, max = %3.3f, avg = %3.3f\n",
760  R_min * dxy, R_max * dxy, R_avg * dxy);
761  m_log->message(2,
762  " Aspect ratios: min = %3.3f, max = %3.3f, avg = %3.3f, max/min = %3.3f\n",
763  R_min, R_max, R_avg, R_max / R_min);
764  }
765 }
766 
767 /*!
768  * Runs the solver and extracts iteration counts.
769  */
771  PetscErrorCode ierr;
772  SolutionInfo result;
773 
774  // Solve the system:
775  ierr = SNESSolve(m_snes, NULL, m_x); PISM_CHK(ierr, "SNESSolve");
776 
777  ierr = SNESGetConvergedReason(m_snes, &result.snes_reason);
778  PISM_CHK(ierr, "SNESGetConvergedReason");
779 
780  ierr = SNESGetIterationNumber(m_snes, &result.snes_it);
781  PISM_CHK(ierr, "SNESGetIterationNumber");
782 
783  ierr = SNESGetLinearSolveIterations(m_snes, &result.ksp_it);
784  PISM_CHK(ierr, "SNESGetLinearSolveIterations");
785 
786  KSP ksp;
787  ierr = SNESGetKSP(m_snes, &ksp);
788  PISM_CHK(ierr, "SNESGetKSP");
789 
790  ierr = KSPGetConvergedReason(ksp, &result.ksp_reason);
791  PISM_CHK(ierr, "KSPGetConvergedReason");
792 
793  PC pc;
794  ierr = KSPGetPC(ksp, &pc);
795  PISM_CHK(ierr, "KSPGetPC");
796 
797  PCType pc_type;
798  ierr = PCGetType(pc, &pc_type);
799  PISM_CHK(ierr, "PCGetType");
800 
801  if (std::string(pc_type) == PCMG) {
802  KSP coarse_ksp;
803  ierr = PCMGGetCoarseSolve(pc, &coarse_ksp);
804  PISM_CHK(ierr, "PCMGGetCoarseSolve");
805 
806  ierr = KSPGetIterationNumber(coarse_ksp, &result.mg_coarse_ksp_it);
807  PISM_CHK(ierr, "KSPGetIterationNumber");
808  } else {
809  result.mg_coarse_ksp_it = 0;
810  }
811 
812  return result;
813 }
814 
816  PetscErrorCode ierr;
817 
818  SolutionInfo info;
819  // total number of SNES and KSP iterations
820  int snes_total_it = 0;
821  int ksp_total_it = 0;
822 
823  // maximum number of continuation steps (input)
824  int Nc = 50;
825 
826  double
827  schoofLen = m_config->get_number("flow_law.Schoof_regularizing_length", "m"),
828  schoofVel = m_config->get_number("flow_law.Schoof_regularizing_velocity", "m second-1"),
829  // desired regularization parameter
830  eps = pow(schoofVel / schoofLen, 2.0),
831  // gamma is a number such that 10^gamma <= eps. It is used to convert lambda in [0, 1] to eps_n
832  gamma = std::floor(std::log10(eps)),
833  // starting value of lambda (input)
834  lambda_min = 0.0,
835  // Final value of lambda (fixed)
836  lambda_max = 1.0,
837  // Minimum step length (input)
838  delta_min = 0.01,
839  // Maximum step length (input)
840  delta_max = 0.2,
841  // Initial increment of lambda (input)
842  delta0 = 0.05,
843  // "Aggressiveness" of the step increase, a non-negative number (input). The effect of
844  // this parameter is linked to the choice of -bp_snes_max_it obtained below.
845  A = 0.25;
846 
847  PetscInt snes_max_it = 0;
848  ierr = SNESGetTolerances(m_snes, NULL, NULL, NULL, &snes_max_it, NULL);
849  PISM_CHK(ierr, "SNESGetTolerances");
850 
851  double lambda = lambda_min;
852  double delta = delta0;
853 
854  // Use the zero initial guess to start
855  ierr = VecSet(m_x, 0.0); PISM_CHK(ierr, "VecSet");
856  ierr = VecSet(m_x_old, 0.0); PISM_CHK(ierr, "VecSet");
857 
858  m_log->message(2,
859  "Blatter solver: Starting parameter continuation with lambda = %f\n",
860  lambda);
861 
862  for (int N = 0; N < Nc; ++N) {
863  // Set the regularization parameter:
864  m_viscosity_eps = std::max(std::pow(10.0, lambda * gamma), eps);
865 
866  m_log->message(2, "Blatter solver: step %d with lambda = %f, eps = %e\n",
867  N, lambda, m_viscosity_eps);
868 
869  // Solve the system:
870 
871  info = solve();
872  snes_total_it += info.snes_it;
873  ksp_total_it += info.ksp_it;
874 
875  // report number of iterations for this continuation step
876  m_log->message(2, "Blatter solver continuation step #%d: %s\n"
877  " lambda = %f, eps = %e\n"
878  " SNES: %d, KSP: %d\n",
879  N, SNESConvergedReasons[info.snes_reason], lambda, m_viscosity_eps,
880  (int)info.snes_it, (int)info.ksp_it);
881  if (info.mg_coarse_ksp_it > 0) {
882  m_log->message(2,
883  " Coarse MG level KSP (last iteration): %d\n",
884  (int)info.mg_coarse_ksp_it);
885  }
886 
887  if (info.snes_reason > 0) {
888  // converged
889 
890  if (m_viscosity_eps <= eps) {
891  // ... while solving the desired (not overregularized) problem
892  info.snes_it = snes_total_it;
893  info.ksp_it = ksp_total_it;
894  return info;
895  }
896 
897  // Store the solution as the "old" initial guess we may need to revert to
898  ierr = VecCopy(m_x, m_x_old); PISM_CHK(ierr, "VecCopy");
899 
900  if (N > 0) {
901  // Adjust delta using the formula from LOCA (equation 2.8 in Salinger2002
902  // corrected using the code in Trilinos).
903  double F = (snes_max_it - info.snes_it) / (double)snes_max_it;
904  delta *= 1.0 + A * F * F;
905  }
906 
907  delta = std::min(delta, delta_max);
908 
909  // Ensure that delta does not take us past lambda_max
910  if (lambda + delta > lambda_max) {
911  delta = lambda_max - lambda;
912  }
913 
914  m_log->message(2, " Advancing lambda from %f to %f (delta = %f)\n",
915  lambda, lambda + delta, delta);
916 
917  lambda += delta;
918  } else if (info.snes_reason == SNES_DIVERGED_LINE_SEARCH or
919  info.snes_reason == SNES_DIVERGED_MAX_IT) {
920  // a continuation step failed
921 
922  // restore the previous initial guess
923  ierr = VecCopy(m_x_old, m_x); PISM_CHK(ierr, "VecCopy");
924 
925  // revert lambda to the previous value
926  lambda -= delta;
927 
928  if (lambda < lambda_min) {
929  m_log->message(2, "Blatter solver: Parameter continuation failed at step 1\n");
930  return info;
931  }
932 
933  if (std::fabs(delta - delta_min) < 1e-6) {
934  m_log->message(2, "Blatter solver: cannot reduce the continuation step\n");
935  return info;
936  }
937 
938  // reduce the step size
939  delta *= 0.5;
940 
941  delta = pism::clip(delta, delta_min, delta_max);
942 
943  lambda += delta;
944  // Note that this delta will not take us past lambda_max because the original
945  // delta satisfies lambda + delta <= lambda_max.
946 
947  m_log->message(2, " Back-tracking to lambda = %f using delta = %f\n",
948  lambda, delta);
949  } else {
950  return info;
951  }
952  }
953 
954  m_log->message(2, "Blatter solver failed after %d parameter continuation steps\n", Nc);
955 
956  return info;
957 }
958 
959 /*!
960  * Disable the Eisenstat-Walker method of setting KSP tolerances.
961  */
962 static void disable_ew(::SNES snes, double rtol) {
963  PetscErrorCode ierr;
964 
965  ierr = SNESKSPSetUseEW(snes, PETSC_FALSE);
966  PISM_CHK(ierr, "SNESKSPSetUseEW");
967 
968  KSP ksp;
969  ierr = SNESGetKSP(snes, &ksp);
970  PISM_CHK(ierr, "SNESGetKSP");
971 
972  ierr = KSPSetTolerances(ksp, rtol, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT);
973  PISM_CHK(ierr, "KSPSetTolerances");
974 }
975 
976 static void enable_ew(::SNES snes) {
977  PetscErrorCode ierr;
978  // restore the old EW setting
979  ierr = SNESKSPSetUseEW(snes, PETSC_TRUE); PISM_CHK(ierr, "SNESKSPSetUseEW");
980 }
981 
982 void Blatter::update(const Inputs &inputs, bool full_update) {
983  PetscErrorCode ierr;
984  (void) full_update;
985 
986  {
987  double
988  schoofLen = m_config->get_number("flow_law.Schoof_regularizing_length", "m"),
989  schoofVel = m_config->get_number("flow_law.Schoof_regularizing_velocity", "m second-1");
990 
991  m_viscosity_eps = pow(schoofVel / schoofLen, 2.0);
992 
993  }
994 
995  init_2d_parameters(inputs);
996  init_ice_hardness(inputs, m_da);
997 
999 
1000  // Store the "old" initial guess: it may be needed to re-try.
1001  ierr = VecCopy(m_x, m_x_old); PISM_CHK(ierr, "VecCopy");
1002 
1003  SolutionInfo info;
1004  int snes_total_it = 0;
1005  int ksp_total_it = 0;
1006 
1007  double ksp_rtol = 1e-5;
1008 
1009  double norm = 0.0;
1010  {
1011  ierr = VecNorm(m_x, NORM_INFINITY, &norm); PISM_CHK(ierr, "VecNorm");
1012  }
1013 
1014  // First attempt
1015  {
1016  if (m_ksp_use_ew and norm == 0.0) {
1017  m_log->message(2,
1018  "Blatter solver: zero initial guess\n"
1019  " Disabling the Eisenstat-Walker method of adjusting solver tolerances\n");
1020 
1021  disable_ew(m_snes, ksp_rtol);
1022  info = solve();
1023  enable_ew(m_snes);
1024  } else {
1025  info = solve();
1026  }
1027  snes_total_it += info.snes_it;
1028  ksp_total_it += info.ksp_it;
1029 
1030  if (info.snes_reason > 0) {
1031  goto bp_done;
1032  }
1033  m_log->message(2, "Blatter solver: %s\n", SNESConvergedReasons[info.snes_reason]);
1034  }
1035 
1036  if (m_ksp_use_ew and norm > 0.0) {
1037  m_log->message(2," Trying without the Eisenstat-Walker method of adjusting solver tolerances\n");
1038 
1039  // restore the previous initial guess
1040  if (not (info.snes_reason == SNES_DIVERGED_LINE_SEARCH or
1041  info.snes_reason == SNES_DIVERGED_MAX_IT)) {
1042  ierr = VecCopy(m_x_old, m_x); PISM_CHK(ierr, "VecCopy");
1043  } else {
1044  // We *keep* the current values in m_x if the line search failed after a few
1045  // iterations or if the solver took too many iterations: no need to discard the
1046  // progress it made before failing.
1047  }
1048 
1049  {
1050  disable_ew(m_snes, ksp_rtol);
1051  info = solve();
1052  enable_ew(m_snes);
1053  }
1054 
1055  snes_total_it += info.snes_it;
1056  ksp_total_it += info.ksp_it;
1057 
1058  if (info.snes_reason > 0) {
1059  goto bp_done;
1060  }
1061  m_log->message(2, "Blatter solver: %s\n", SNESConvergedReasons[info.snes_reason]);
1062  }
1063 
1064  // try using parameter continuation
1065  {
1066  info = parameter_continuation();
1067  snes_total_it += info.snes_it;
1068  ksp_total_it += info.ksp_it;
1069  if (info.snes_reason > 0) {
1070  goto bp_done;
1071  }
1072  m_log->message(2, "Blatter solver: %s\n", SNESConvergedReasons[info.snes_reason]);
1073  }
1074 
1075  throw RuntimeError(PISM_ERROR_LOCATION, "Blatter solver failed");
1076 
1077  bp_done:
1078  // report the total number of iterations
1079  m_log->message(2,
1080  "Blatter solver: %s. Done.\n"
1081  " SNES: %d, KSP: %d\n",
1082  SNESConvergedReasons[info.snes_reason],
1083  snes_total_it, ksp_total_it);
1084  if (info.mg_coarse_ksp_it > 0) {
1085  m_log->message(2,
1086  " Level 0 KSP (last iteration): %d\n",
1087  (int)info.mg_coarse_ksp_it);
1088  }
1089 
1090  // put basal velocity in m_velocity to use it in the next call
1092 
1094  inputs.geometry->cell_type,
1096 
1098 
1099  // copy the solution from m_x to m_u_sigma, m_v_sigma for re-starting
1100  copy_solution();
1101 }
1102 
1104  Vector2d ***x = nullptr;
1105  int ierr = DMDAVecGetArray(m_da, m_x, &x); PISM_CHK(ierr, "DMDAVecGetArray");
1106 
1107  int Mz = m_u_sigma->levels().size();
1108 
1109  array::AccessScope list{m_u_sigma.get(), m_v_sigma.get()};
1110 
1111  for (auto p = m_grid->points(); p; p.next()) {
1112  const int i = p.i(), j = p.j();
1113 
1114  auto *u = m_u_sigma->get_column(i, j);
1115  auto *v = m_v_sigma->get_column(i, j);
1116 
1117  for (int k = 0; k < Mz; ++k) {
1118  u[k] = x[j][i][k].u; // STORAGE_ORDER
1119  v[k] = x[j][i][k].v; // STORAGE_ORDER
1120  }
1121  }
1122 
1123  ierr = DMDAVecRestoreArray(m_da, m_x, &x); PISM_CHK(ierr, "DMDAVecRestoreArray");
1124 }
1125 
1127  Vector2d ***x = nullptr;
1128  int ierr = DMDAVecGetArray(m_da, m_x, &x); PISM_CHK(ierr, "DMDAVecGetArray");
1129 
1130  array::AccessScope list{&result};
1131 
1132  for (auto p = m_grid->points(); p; p.next()) {
1133  const int i = p.i(), j = p.j();
1134 
1135  result(i, j) = x[j][i][0]; // STORAGE_ORDER
1136  }
1137 
1138  ierr = DMDAVecRestoreArray(m_da, m_x, &x); PISM_CHK(ierr, "DMDAVecRestoreArray");
1139 }
1140 
1141 
1143  const array::Array3D &v_sigma) {
1144  Vector2d ***x = nullptr;
1145  int ierr = DMDAVecGetArray(m_da, m_x, &x); PISM_CHK(ierr, "DMDAVecGetArray");
1146 
1147  int Mz = m_u_sigma->levels().size();
1148 
1149  array::AccessScope list{&u_sigma, &v_sigma};
1150 
1151  for (auto p = m_grid->points(); p; p.next()) {
1152  const int i = p.i(), j = p.j();
1153 
1154  const auto *u = u_sigma.get_column(i, j);
1155  const auto *v = v_sigma.get_column(i, j);
1156 
1157  for (int k = 0; k < Mz; ++k) {
1158  x[j][i][k].u = u[k]; // STORAGE_ORDER
1159  x[j][i][k].v = v[k]; // STORAGE_ORDER
1160  }
1161  }
1162 
1163  ierr = DMDAVecRestoreArray(m_da, m_x, &x); PISM_CHK(ierr, "DMDAVecRestoreArray");
1164 }
1165 
1167  PetscErrorCode ierr;
1168 
1169  Vector2d ***x = nullptr;
1170  ierr = DMDAVecGetArray(m_da, m_x, &x); PISM_CHK(ierr, "DMDAVecGetArray");
1171 
1172  int Mz = m_u_sigma->levels().size();
1173 
1174  array::AccessScope list{&result, &m_parameters};
1175 
1176  for (auto p = m_grid->points(); p; p.next()) {
1177  const int i = p.i(), j = p.j();
1178 
1179  double H = m_parameters(i, j).thickness;
1180 
1181  Vector2d V(0.0, 0.0);
1182 
1183  if (H > 0.0) {
1184  // use trapezoid rule to compute the column average
1185  double dz = H / (Mz - 1);
1186  for (int k = 0; k < Mz - 1; ++k) {
1187  V += x[j][i][k] + x[j][i][k + 1]; // STORAGE_ORDER
1188  }
1189  V *= (0.5 * dz) / H;
1190  }
1191 
1192  result(i, j) = V;
1193  }
1194 
1195  ierr = DMDAVecRestoreArray(m_da, m_x, &x); PISM_CHK(ierr, "DMDAVecRestoreArray");
1196 
1197  result.update_ghosts();
1198 }
1199 
1200 
1201 std::shared_ptr<array::Array3D> Blatter::velocity_u_sigma() const {
1202  return m_u_sigma;
1203 }
1204 
1205 std::shared_ptr<array::Array3D> Blatter::velocity_v_sigma() const {
1206  return m_v_sigma;
1207 }
1208 
1209 } // end of namespace stressbalance
1210 } // end of namespace pism
#define ICE_GOLDSBY_KOHLSTEDT
std::shared_ptr< const Grid > grid() const
Definition: Component.cc:105
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
VariableLookupData find_variable(const std::string &short_name, const std::string &std_name) const
Find a variable using its standard name and/or short name.
Definition: File.cc:361
unsigned int nrecords() const
Get the number of records. Uses the length of an unlimited dimension.
Definition: File.cc:313
High-level PISM I/O class.
Definition: File.hh:56
array::Scalar1 sea_level_elevation
Definition: Geometry.hh:48
array::CellType2 cell_type
Definition: Geometry.hh:55
array::Scalar2 ice_thickness
Definition: Geometry.hh:51
array::Scalar2 bed_elevation
Definition: Geometry.hh:47
This class represents a 2D vector field (such as ice velocity) at a certain grid point.
Definition: Vector2d.hh:29
T * rawptr()
Definition: Wrapper.hh:39
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition: Array.hh:65
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
const std::vector< double > & levels() const
Definition: Array.cc:140
void set(double c)
Result: v[j] <- c for all j.
Definition: Array.cc:707
void update_ghosts()
Updates ghost points.
Definition: Array.cc:693
void local_to_global(unsigned int k, int &i, int &j) const
Convert a local degree of freedom index k to a global degree of freedom index (i,j).
Definition: Element.hh:171
void reset(int i, int j)
Initialize the Element to element (i, j) for the purposes of inserting into global residual and Jacob...
Definition: Element.cc:187
void nodal_values(const array::Scalar &x_global, int *result) const
Get nodal values of an integer mask.
Definition: Element.cc:176
GlobalIndex local_to_global(int i, int j, int k, unsigned int n) const
Definition: Element.hh:298
Q1 element with sides parallel to X and Y axes.
Definition: Element.hh:257
int n_pts() const
Number of quadrature points.
Definition: Element.hh:404
3D Q1 element
Definition: Element.hh:351
The 1-point Gaussian quadrature on the square [-1,1]*[-1,1].
Definition: Quadrature.hh:89
std::shared_ptr< FlowLaw > create()
void remove(const std::string &name)
void define_model_state_impl(const File &output) const
The default (empty implementation).
Definition: Blatter.cc:691
void get_basal_velocity(array::Vector &result)
Definition: Blatter.cc:1126
virtual Vector2d u_bc(double x, double y, double z) const
Definition: Blatter.cc:132
void compute_node_type(double min_thickness)
Definition: Blatter.cc:56
void write_model_state_impl(const File &output) const
The default (empty implementation).
Definition: Blatter.cc:696
virtual bool marine_boundary(int face, const int *node_type, const double *ice_bottom, const double *sea_level)
Definition: Blatter.cc:225
fem::Q1Element3Face m_face4
Definition: Blatter.hh:112
static bool partially_submerged_face(int face, const double *z, const double *sea_level)
Definition: Blatter.cc:192
virtual void init_2d_parameters(const Inputs &inputs)
Definition: Blatter.cc:532
std::shared_ptr< array::Array3D > m_u_sigma
Definition: Blatter.hh:68
std::shared_ptr< array::Array3D > velocity_v_sigma() const
Definition: Blatter.cc:1205
SolutionInfo parameter_continuation()
Definition: Blatter.cc:815
void update(const Inputs &inputs, bool)
Definition: Blatter.cc:982
PetscErrorCode setup(DM pism_da, grid::Periodicity p, int Mz, int coarsening_factor, const std::string &prefix)
Definition: Blatter.cc:382
void init_ice_hardness(const Inputs &inputs, const petsc::DM &da)
Definition: Blatter.cc:579
static PetscErrorCode jacobian_callback(DMDALocalInfo *info, const Vector2d ***x, Mat A, Mat J, Blatter *solver)
Definition: jacobian.cc:354
static PetscErrorCode function_callback(DMDALocalInfo *info, const Vector2d ***x, Vector2d ***f, Blatter *solver)
Definition: residual.cc:457
static const int m_Nq
Definition: Blatter.hh:105
fem::Q1Element3Face m_face100
Definition: Blatter.hh:113
void compute_averaged_velocity(array::Vector &result)
Definition: Blatter.cc:1166
std::shared_ptr< array::Array3D > velocity_u_sigma() const
Definition: Blatter.cc:1201
Blatter(std::shared_ptr< const Grid > grid, int Mz, int coarsening_factor)
Definition: Blatter.cc:258
static bool exterior_element(const int *node_type)
Definition: Blatter.cc:146
static bool grounding_line(const double *F)
Definition: Blatter.cc:166
array::Array2D< Parameters > m_parameters
Definition: Blatter.hh:79
virtual bool dirichlet_node(const DMDALocalInfo &info, const fem::Element3::GlobalIndex &I)
Definition: Blatter.cc:124
std::shared_ptr< array::Array3D > m_v_sigma
Definition: Blatter.hh:68
virtual void nodal_parameter_values(const fem::Q1Element3 &element, Parameters **P, int i, int j, int *node_type, double *bottom, double *thickness, double *surface, double *sea_level) const
Definition: Blatter.cc:635
void set_initial_guess(const array::Array3D &u_sigma, const array::Array3D &v_sigma)
Definition: Blatter.cc:1142
const array::Scalar * basal_yield_stress
const array::Array3D * enthalpy
void compute_basal_frictional_heating(const array::Vector &velocity, const array::Scalar &tauc, const array::CellType &mask, array::Scalar &result) const
Compute the basal frictional heating.
std::shared_ptr< rheology::FlowLaw > m_flow_law
Shallow stress balance (such as the SSA).
#define PISM_CHK(errcode, name)
#define PISM_ERROR_LOCATION
#define n
Definition: exactTestM.c:37
double max(const array::Scalar &input)
Finds maximum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:165
@ WITH_GHOSTS
Definition: Array.hh:62
@ WITHOUT_GHOSTS
Definition: Array.hh:62
double min(const array::Scalar &input)
Finds minimum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:193
const int n_chi
Number of shape functions on a Q1 element.
Definition: FEM.hh:213
const unsigned int incident_nodes[n_faces][4]
Definition: FEM.hh:223
const int n_chi
Definition: FEM.hh:191
Periodicity
Definition: Grid.hh:51
@ Y_PERIODIC
Definition: Grid.hh:51
@ X_PERIODIC
Definition: Grid.hh:51
@ PISM_GUESS
Definition: IO_Flags.hh:56
@ PISM_READONLY
open an existing file for reading only
Definition: IO_Flags.hh:72
@ PISM_DOUBLE
Definition: IO_Flags.hh:52
bool grounded(int M)
Grounded cell (grounded ice or ice-free).
Definition: Mask.hh:44
static void disable_ew(::SNES snes, double rtol)
Definition: Blatter.cc:962
static void enable_ew(::SNES snes)
Definition: Blatter.cc:976
static PetscErrorCode blatter_restriction_hook(DM fine, Mat mrestrict, Vec rscale, Mat inject, DM coarse, void *ctx)
Definition: Blatter.cc:344
static PetscErrorCode blatter_coarsening_hook(DM dm_fine, DM dm_coarse, void *ctx)
Definition: Blatter.cc:358
double grid_z(double b, double H, int Mz, int k)
PetscErrorCode setup_level(DM dm, int mg_levels)
static double F(double SL, double B, double H, double alpha)
@ NODE_BOUNDARY
Definition: node_types.hh:35
@ NODE_EXTERIOR
Definition: node_types.hh:36
@ NODE_INTERIOR
Definition: node_types.hh:34
@ NOT_GHOSTED
Definition: DataAccess.hh:30
InputOptions process_input_options(MPI_Comm com, Config::ConstPtr config)
Definition: Component.cc:43
static const double g
Definition: exactTestP.cc:36
DMDALocalInfo grid_transpose(const DMDALocalInfo &input)
void GlobalMax(MPI_Comm comm, double *local, double *result, int count)
@ INIT_RESTART
Definition: Component.hh:56
T clip(T x, T a, T b)
std::string printf(const char *format,...)
static const double k
Definition: exactTestP.cc:42
PetscErrorCode restrict_data(DM fine, DM coarse, const char *dm_name)
Restrict model parameters from the fine grid onto the coarse grid.
void GlobalMin(MPI_Comm comm, double *local, double *result, int count)
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)
PetscErrorCode create_restriction(DM fine, DM coarse, const char *dm_name)
Create the restriction matrix.
const int I[]
Definition: ssafd_code.cc:24
InitializationType type
initialization type
Definition: Component.hh:61
std::string filename
name of the input file (if applicable)
Definition: Component.hh:63