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