Loading [MathJax]/extensions/tex2jax.js
PISM, A Parallel Ice Sheet Model 2.2.2-d6b3a29ca committed by Constantine Khrulev on 2025-03-28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
residual.cc
Go to the documentation of this file.
1/* Copyright (C) 2020, 2021, 2023, 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 "pism/stressbalance/blatter/Blatter.hh"
21
22#include "pism/basalstrength/basal_resistance.hh"
23#include "pism/rheology/FlowLaw.hh"
24#include "pism/util/node_types.hh"
25#include "pism/stressbalance/blatter/util/DataAccess.hh"
26#include "pism/stressbalance/blatter/util/grid_hierarchy.hh" // grid_transpose(), grid_z()
27#include "pism/util/fem/Quadrature.hh"
28
29namespace pism {
30namespace stressbalance {
31
32static const Vector2d u_exterior = {0.0, 0.0};
33
34/*!
35 * Computes the residual contribution of the "main" part of the Blatter system.
36 */
38 const Vector2d *u_nodal,
39 const double *B_nodal,
40 Vector2d *residual) {
41
43 *u = m_work2[0],
44 *u_x = m_work2[1],
45 *u_y = m_work2[2],
46 *u_z = m_work2[3];
47
48 double *B = m_work[0];
49
50 // evaluate u and its partial derivatives at quadrature points
51 element.evaluate(u_nodal, u, u_x, u_y, u_z);
52
53 // evaluate B (ice hardness) at quadrature points
54 element.evaluate(B_nodal, B);
55
56 // loop over all quadrature points
57 for (unsigned int q = 0; q < element.n_pts(); ++q) {
58 auto W = element.weight(q) / m_scaling;
59
60 double
61 ux = u_x[q].u,
62 uy = u_y[q].u,
63 uz = u_z[q].u,
64 vx = u_x[q].v,
65 vy = u_y[q].v,
66 vz = u_z[q].v;
67
68 double gamma = (ux * ux + vy * vy + ux * vy +
69 0.25 * ((uy + vx) * (uy + vx) + uz * uz + vz * vz));
70
71 double eta;
72 m_flow_law->effective_viscosity(B[q], gamma, m_viscosity_eps, &eta, nullptr);
73
74 // add the enhancement factor
75 eta *= m_E_viscosity;
76
77 // loop over all test functions
78 for (int t = 0; t < element.n_chi(); ++t) {
79 const auto &psi = element.chi(q, t);
80
81 residual[t].u += W * (eta * (psi.dx * (4.0 * ux + 2.0 * vy) +
82 psi.dy * (uy + vx) +
83 psi.dz * uz));
84 residual[t].v += W * (eta * (psi.dx * (uy + vx) +
85 psi.dy * (2.0 * ux + 4.0 * vy) +
86 psi.dz * vz));
87 }
88 }
89}
90
91/*! Computes the residual contribution of the "source term".
92 *
93 * This term contains the driving stress.
94 */
96 const double *surface,
97 const double *bed,
98 Vector2d *residual) {
99
100 // compute s_x and s_y
101 double
102 *s_x = m_work[0],
103 *s_y = m_work[1];
104
105 if (m_eta_transform) {
106 // use the same storage for eta_x and eta_y
107 double
108 *eta_nodal = m_work[2],
109 *eta = m_work[3],
110 *eta_x = s_x,
111 *eta_y = s_y,
112 *eta_z = m_work[4];
113 double
114 *b = m_work[5],
115 *b_x = m_work[6],
116 *b_y = m_work[7],
117 *b_z = m_work[8];
118
119 double
121 p = (2.0 * n + 2.0) / n;
122
123 for (int k = 0; k < element.n_chi(); ++k) {
124 eta_nodal[k] = pow(surface[k] - bed[k], p);
125 }
126
127 element.evaluate(eta_nodal, eta, eta_x, eta_y, eta_z);
128 element.evaluate(bed, b, b_x, b_y, b_z);
129
130 for (unsigned int q = 0; q < element.n_pts(); ++q) {
131 double C = pow(eta[q], 1.0 / p - 1.0) / p;
132
133 s_x[q] = C * eta_x[q] + b_x[q];
134 s_y[q] = C * eta_y[q] + b_y[q];
135 }
136 } else {
137 // these arrays are needed by the call below (but results are discarded)
138 double
139 *s = m_work[2],
140 *s_z = m_work[3];
141
142 element.evaluate(surface, s, s_x, s_y, s_z);
143 }
144
145 for (unsigned int q = 0; q < element.n_pts(); ++q) {
146 auto W = element.weight(q) / m_scaling;
147
148 auto F = m_rho_ice_g * Vector2d(s_x[q], s_y[q]);
149
150 // loop over all test functions
151 for (int t = 0; t < element.n_chi(); ++t) {
152 const auto &psi = element.chi(q, t);
153
154 residual[t] += W * psi.val * F;
155 }
156 }
157}
158
159/*!
160 * Computes the residual contribution of the basal boundary condition.
161 *
162 * This takes care of basal sliding.
163 */
165 const fem::Q1Element3Face &face,
166 const double *tauc_nodal,
167 const double *f_nodal,
168 const Vector2d *u_nodal,
169 Vector2d *residual) {
170
171 Vector2d *u = m_work2[0];
172
173 double
174 *tauc = m_work[0],
175 *floatation = m_work[1];
176
177 face.evaluate(u_nodal, u);
178 face.evaluate(tauc_nodal, tauc);
179 face.evaluate(f_nodal, floatation);
180
181 for (unsigned int q = 0; q < face.n_pts(); ++q) {
182 auto W = face.weight(q) / m_scaling;
183
184 bool grounded = floatation[q] <= 0.0;
185 double beta = grounded ? m_basal_sliding_law->drag(tauc[q], u[q].u, u[q].v) : 0.0;
186
187 // loop over all test functions
188 for (int t = 0; t < element.n_chi(); ++t) {
189 auto psi = face.chi(q, t);
190
191 residual[t] += W * psi * beta * u[q];
192 }
193 }
194}
195
196/*!
197 * Top surface contribution to the residual.
198 *
199 * Used by verification tests ONLY.
200 */
202 const fem::Q1Element3Face &face,
203 Vector2d *residual) {
204 (void) element;
205 (void) face;
206 (void) residual;
207 // In normal circumstances the top surface contribution is zero (natural BCs apply).
208}
209
210
211/*!
212 * Computes the residual contribution of lateral boundary conditions.
213 *
214 * This takes care of "calving front" stress boundary conditions.
215 *
216 * FIXME: make p_ocean an input from a parameterization of the melange back pressure.
217 */
219 const fem::Q1Element3Face &face,
220 const double *surface_nodal,
221 const double *z_nodal,
222 const double *sl_nodal,
223 Vector2d *residual) {
224 double
225 *z = m_work[0],
226 *s = m_work[1],
227 *sea_level = m_work[2];
228
229 // compute physical coordinates of quadrature points on this face
230 face.evaluate(surface_nodal, s);
231 face.evaluate(z_nodal, z);
232 face.evaluate(sl_nodal, sea_level);
233
234 // loop over all quadrature points
235 for (unsigned int q = 0; q < face.n_pts(); ++q) {
236 auto W = face.weight(q) / m_scaling;
237 auto N3 = face.normal(q);
238 Vector2d N = {N3.x, N3.y};
239
240 double
241 ice_depth = s[q] - z[q],
242 p_ice = m_rho_ice_g * ice_depth,
243 water_depth = std::max(sea_level[q] - z[q], 0.0),
244 p_ocean = m_rho_ocean_g * water_depth;
245
246 // loop over all test functions
247 for (int t = 0; t < element.n_chi(); ++t) {
248 auto psi = face.chi(q, t);
249
250 residual[t] += - W * psi * (p_ice - p_ocean) * N;
251 }
252 }
253}
254
255/*! Set the residual at Dirichlet locations
256 *
257 * Compute the residual at Dirichlet locations and reset the residual to zero elsewhere.
258 *
259 * Setting it to zero is necessary because we call DMDASNESSetFunctionLocal() with
260 * INSERT_VALUES.
261 *
262 */
263void Blatter::residual_dirichlet(const DMDALocalInfo &info,
264 Parameters **P,
265 const Vector2d ***x,
266 Vector2d ***R) {
267
268 double
269 x_min = m_grid->x0() - m_grid->Lx(),
270 y_min = m_grid->y0() - m_grid->Ly(),
271 dx = m_grid->dx(),
272 dy = m_grid->dy(),
273 scaling = 1.0;
274
275 // Compute the residual at Dirichlet BC nodes and reset the residual to zero elsewhere.
276 //
277 // here we loop over all the *owned* nodes
278 for (int j = info.ys; j < info.ys + info.ym; j++) {
279 for (int i = info.xs; i < info.xs + info.xm; i++) {
280
281 // compute the residual at ice-free map plane locations
282 if ((int)P[j][i].node_type == NODE_EXTERIOR) {
283 for (int k = info.zs; k < info.zs + info.zm; k++) {
284 R[j][i][k] = scaling * (x[j][i][k] - u_exterior); // STORAGE_ORDER
285 }
286 continue;
287 }
288
289 for (int k = info.zs; k < info.zs + info.zm; k++) {
290 // reset to zero
291 R[j][i][k] = 0.0; // STORAGE_ORDER
292
293 // compute the residual at Dirichlet nodes in verification tests
294 if (dirichlet_node(info, {i, j, k})) {
295 double
296 xx = x_min + i * dx,
297 yy = y_min + j * dy,
298 b = P[j][i].bed,
299 H = P[j][i].thickness,
300 zz = grid_z(b, H, info.mz, k);
301 Vector2d U_bc = u_bc(xx, yy, zz);
302 R[j][i][k] = scaling * (x[j][i][k] - U_bc); // STORAGE_ORDER
303 }
304 }
305 }
306 }
307}
308
309/*!
310 * Computes the residual.
311 */
312void Blatter::compute_residual(DMDALocalInfo *petsc_info,
313 const Vector2d ***X, Vector2d ***R) {
314 auto info = grid_transpose(*petsc_info);
315
316 // Stencil width of 1 is not very important, but if info.sw > 1 will lead to more
317 // redundant computation (we would be looping over elements that don't contribute to any
318 // owned nodes).
319 assert(info.sw == 1);
320
321 // horizontal grid spacing is the same on all multigrid levels
322 double
323 x_min = m_grid->x0() - m_grid->Lx(),
324 y_min = m_grid->y0() - m_grid->Ly(),
325 dx = m_grid->dx(),
326 dy = m_grid->dy();
327
329 dx, dy, x_min, y_min);
330
331 // Number of nodes per element.
332 const int Nk = fem::q13d::n_chi;
333 assert(element.n_chi() <= Nk);
334 assert(element.n_pts() <= m_Nq);
335
336 // scalar quantities
337 double z[Nk];
338 double floatation[Nk], sea_level[Nk], bottom_elevation[Nk], ice_thickness[Nk], surface_elevation[Nk];
339 double B[Nk], basal_yield_stress[Nk];
340 int node_type[Nk];
341
342 // 2D vector quantities
343 Vector2d velocity[Nk], R_nodal[Nk];
344
345 // note: we use info.da below because ice hardness is on the grid corresponding to the
346 // current multigrid level
347 //
348 // FIXME: This communicates ghosts of ice hardness
349 DataAccess<double***> ice_hardness(info.da, 3, GHOSTED);
350
352 auto *P = m_parameters.array();
353
354 // Compute the residual at Dirichlet nodes and set it to zero elsewhere.
355 residual_dirichlet(info, P, X, R);
356
357 // loop over all the elements that have at least one owned node
358 for (int j = info.gys; j < info.gys + info.gym - 1; j++) {
359 for (int i = info.gxs; i < info.gxs + info.gxm - 1; i++) {
360
361 // Initialize 2D geometric info at element nodes
362 nodal_parameter_values(element, P, i, j,
363 node_type,
364 bottom_elevation,
365 ice_thickness,
366 surface_elevation,
367 sea_level);
368
369 // skip ice-free (exterior) elements
370 if (exterior_element(node_type)) {
371 continue;
372 }
373
374 // loop over elements in a column
375 for (int k = info.gzs; k < info.gzs + info.gzm - 1; k++) {
376
377 // Reset element residual to zero in preparation.
378 for (int n = 0; n < Nk; ++n) {
379 R_nodal[n] = 0.0;
380 }
381
382 // Compute z coordinates of the nodes of this element
383 for (int n = 0; n < Nk; ++n) {
384 auto I = element.local_to_global(i, j, k, n);
385 z[n] = grid_z(bottom_elevation[n], ice_thickness[n], info.mz, I.k);
386 }
387
388 // Compute values of chi, chi_x, chi_y, chi_z and quadrature weights at quadrature
389 // points on this physical element
390 element.reset(i, j, k, z);
391
392 // Get nodal values of ice velocity.
393 {
394 element.nodal_values(X, velocity);
395
396 // Take care of Dirichlet BC: don't contribute to Dirichlet nodes and set nodal
397 // values of the current iterate to Dirichlet BC values.
398 for (int n = 0; n < Nk; ++n) {
399 auto I = element.local_to_global(n);
400 if (dirichlet_node(info, I)) {
401 element.mark_row_invalid(n);
402 velocity[n] = u_bc(element.x(n), element.y(n), element.z(n));
403 }
404 }
405 }
406
407 // Get nodal values of ice hardness.
408 element.nodal_values((double***)ice_hardness, B);
409
410 // "main" part of the residual
411 residual_f(element, velocity, B, R_nodal);
412
413 // the "source term" (driving stress)
414 residual_source_term(element, surface_elevation, bottom_elevation, R_nodal);
415
416 // basal boundary
417 if (k == 0) {
418 for (int n = 0; n < Nk; ++n) {
419 auto I = element.local_to_global(n);
420
421 basal_yield_stress[n] = P[I.j][I.i].tauc;
422 floatation[n] = P[I.j][I.i].floatation;
423 }
424
425 // use an N*N-point equally-spaced quadrature at grounding lines
426 fem::Q1Element3Face *face = grounding_line(floatation) ? &m_face100 : &m_face4;
428
429 residual_basal(element, *face, basal_yield_stress, floatation, velocity, R_nodal);
430 }
431
432 // lateral boundary
433 // loop over all vertical faces (see fem::q13d::incident_nodes for the order)
434 for (int f = 0; f < 4; ++f) {
435 if (marine_boundary(f, node_type, bottom_elevation, sea_level)) {
436 // use an N*N-point equally-spaced quadrature for partially-submerged faces
437 fem::Q1Element3Face *face = (partially_submerged_face(f, z, sea_level) ?
438 &m_face100 : &m_face4);
439 face->reset(f, z);
440
441 residual_lateral(element, *face, surface_elevation, z, sea_level, R_nodal);
442 }
443 } // end of the loop over element faces
444
445 // top boundary (verification tests only)
446 if (k == info.mz - 2) {
448
449 residual_surface(element, m_face4, R_nodal);
450 }
451
452 element.add_contribution(R_nodal, R);
453 } // end of the loop over i
454 } // end of the loop over j
455 } // end of the loop over k
456}
457
458PetscErrorCode Blatter::function_callback(DMDALocalInfo *info,
459 const Vector2d ***x, Vector2d ***f,
460 Blatter *solver) {
461 try {
462 solver->compute_residual(info, x, f);
463 } catch (...) {
464 MPI_Comm com = solver->grid()->com;
466 SETERRQ(com, 1, "A PISM callback failed");
467 }
468 return 0;
469}
470
471} // end of namespace stressbalance
472} // end of namespace pism
std::shared_ptr< const Grid > grid() const
Definition Component.cc:105
const std::shared_ptr< const Grid > m_grid
grid used by this component
Definition Component.hh:156
virtual double drag(double tauc, double vx, double vy) const
Compute the drag coefficient for the basal shear stress.
This class represents a 2D vector field (such as ice velocity) at a certain grid point.
Definition Vector2d.hh:29
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition Array.hh:64
void nodal_values(T const *const *const *x_global, T *result) const
Extract nodal values for the element (i,j,k) from global array x_global into the element-local array ...
Definition Element.hh:321
void add_contribution(const T *local, T ***y_global) const
Add the values of element-local contributions y to the global vector y_global.
Definition Element.hh:335
GlobalIndex local_to_global(int i, int j, int k, unsigned int n) const
Definition Element.hh:305
void evaluate(const T *x, T *vals, T *dx, T *dy, T *dz) const
Given nodal values, compute the values and partial derivatives at the quadrature points.
Definition Element.hh:285
int n_chi() const
Definition Element.hh:65
double weight(unsigned int q) const
Weight of the quadrature point q
Definition Element.hh:85
const Germ & chi(unsigned int q, unsigned int k) const
Definition Element.hh:73
unsigned int n_pts() const
Number of quadrature points.
Definition Element.hh:80
double weight(unsigned int q) const
Definition Element.hh:414
unsigned int n_pts() const
Number of quadrature points.
Definition Element.hh:411
const double & chi(unsigned int q, unsigned int k) const
Definition Element.hh:419
void evaluate(const T *x, T *result) const
Definition Element.hh:434
const Vector3 & normal(unsigned int q) const
Definition Element.hh:426
void reset(int face, const double *z)
Definition Element.cc:543
void reset(int i, int j, int k, const double *z)
Definition Element.cc:460
double y(int n) const
Definition Element.hh:376
double z(int n) const
Definition Element.hh:381
void mark_row_invalid(unsigned int k)
Mark that the row corresponding to local degree of freedom k should not be updated when inserting int...
Definition Element.cc:221
double x(int n) const
Definition Element.hh:371
3D Q1 element
Definition Element.hh:358
virtual void residual_f(const fem::Q1Element3 &element, const Vector2d *u_nodal, const double *B_nodal, Vector2d *residual)
Definition residual.cc:37
virtual Vector2d u_bc(double x, double y, double z) const
Definition Blatter.cc:133
virtual void residual_source_term(const fem::Q1Element3 &element, const double *surface, const double *bed, Vector2d *residual)
Definition residual.cc:95
virtual void residual_basal(const fem::Q1Element3 &element, const fem::Q1Element3Face &face, const double *tauc_nodal, const double *f_nodal, const Vector2d *u_nodal, Vector2d *residual)
Definition residual.cc:164
void compute_residual(DMDALocalInfo *info, const Vector2d ***X, Vector2d ***R)
Definition residual.cc:312
virtual bool marine_boundary(int face, const int *node_type, const double *ice_bottom, const double *sea_level)
Definition Blatter.cc:226
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:193
virtual void residual_lateral(const fem::Q1Element3 &element, const fem::Q1Element3Face &face, const double *surface_nodal, const double *z_nodal, const double *sl_nodal, Vector2d *residual)
Definition residual.cc:218
double m_work[m_n_work][m_Nq]
Definition Blatter.hh:109
void residual_dirichlet(const DMDALocalInfo &info, Parameters **P, const Vector2d ***x, Vector2d ***R)
Definition residual.cc:263
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
static bool exterior_element(const int *node_type)
Definition Blatter.cc:147
static bool grounding_line(const double *F)
Definition Blatter.cc:167
array::Array2D< Parameters > m_parameters
Definition Blatter.hh:80
virtual bool dirichlet_node(const DMDALocalInfo &info, const fem::Element3::GlobalIndex &I)
Definition Blatter.cc:125
virtual void residual_surface(const fem::Q1Element3 &element, const fem::Q1Element3Face &face, Vector2d *residual)
Definition residual.cc:201
Vector2d m_work2[m_n_work][m_Nq]
Definition Blatter.hh:111
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:636
const array::Vector1 & velocity() const
Get the thickness-advective 2D velocity.
std::shared_ptr< rheology::FlowLaw > m_flow_law
IceBasalResistancePlasticLaw * m_basal_sliding_law
#define n
Definition exactTestM.c:37
const int n_chi
Number of shape functions on a Q1 element.
Definition FEM.hh:213
static const Vector2d u_exterior
Definition residual.cc:32
double grid_z(double b, double H, int Mz, int k)
static double F(double SL, double B, double H, double alpha)
@ NODE_EXTERIOR
Definition node_types.hh:36
@ GHOSTED
Definition DataAccess.hh:30
DMDALocalInfo grid_transpose(const DMDALocalInfo &input)
static const double k
Definition exactTestP.cc:42
void handle_fatal_errors(MPI_Comm com)