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
jacobian.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
26#include "pism/stressbalance/blatter/util/DataAccess.hh"
27#include "pism/stressbalance/blatter/util/grid_hierarchy.hh" // grid_transpose(), grid_z()
28#include "pism/util/fem/Quadrature.hh"
29
30namespace pism {
31namespace stressbalance {
32
33/*!
34 * Computes the Jacobian contribution of the "main" part of the Blatter system.
35 */
37 const Vector2d *u_nodal,
38 const double *B_nodal,
39 double K[16][16]) {
40 int Nk = fem::q13d::n_chi;
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 element.evaluate(u_nodal, u, u_x, u_y, u_z);
51 element.evaluate(B_nodal, B);
52
53 // loop over all quadrature points
54 for (unsigned int q = 0; q < element.n_pts(); ++q) {
55 auto W = element.weight(q) / m_scaling;
56
57 double
58 ux = u_x[q].u,
59 uy = u_y[q].u,
60 uz = u_z[q].u,
61 vx = u_x[q].v,
62 vy = u_y[q].v,
63 vz = u_z[q].v;
64
65 double gamma = (ux * ux + vy * vy + ux * vy +
66 0.25 * ((uy + vx) * (uy + vx) + uz * uz + vz * vz));
67
68 double eta, deta;
69 m_flow_law->effective_viscosity(B[q], gamma, m_viscosity_eps, &eta, &deta);
70
71 // add the enhancement factor
72 eta *= m_E_viscosity;
73 deta *= m_E_viscosity;
74
75 // loop over test and trial functions, computing the upper-triangular part of
76 // the element Jacobian
77 for (int t = 0; t < Nk; ++t) {
78 auto psi = element.chi(q, t);
79 for (int s = t; s < Nk; ++s) {
80 auto phi = element.chi(q, s);
81
82 // partial derivatives of gamma with respect to u_i and v_i
83 double
84 gamma_u = 2.0 * ux * phi.dx + vy * phi.dx + 0.5 * phi.dy * (uy + vx) + 0.5 * uz * phi.dz,
85 gamma_v = 2.0 * vy * phi.dy + ux * phi.dy + 0.5 * phi.dx * (uy + vx) + 0.5 * vz * phi.dz;
86
87 // partial derivatives of eta with respect to u_i and v_i, using chain rule
88 double
89 eta_u = deta * gamma_u,
90 eta_v = deta * gamma_v;
91
92 // F_u = grad(psi) . (4ux + 2vy, uy + vx, uz) and
93 // F_v = grad(psi) . (uy + vx, 4vy + 2ux, vz)
94 double
95 F_u = (psi.dx * (4.0 * ux + 2.0 * vy) + psi.dy * (uy + vx) + psi.dz * uz),
96 F_v = (psi.dx * (uy + vx) + psi.dy * (4.0 * vy + 2.0 * ux) + psi.dz * vz);
97
98 // partial derivatives of F_u with respect to u_i and v_i
99 double
100 F_uu = 4.0 * psi.dx * phi.dx + psi.dy * phi.dy + psi.dz * phi.dz,
101 F_uv = 2.0 * psi.dx * phi.dy + psi.dy * phi.dx;
102
103 // partial derivatives of F_v with respect to u_i and v_i
104 double
105 F_vu = 2.0 * psi.dy * phi.dx + psi.dx * phi.dy,
106 F_vv = 4.0 * psi.dy * phi.dy + psi.dx * phi.dx + psi.dz * phi.dz;
107
108 K[t * 2 + 0][s * 2 + 0] += W * (eta * F_uu + eta_u * F_u);
109 K[t * 2 + 0][s * 2 + 1] += W * (eta * F_uv + eta_v * F_u);
110 K[t * 2 + 1][s * 2 + 0] += W * (eta * F_vu + eta_u * F_v);
111 K[t * 2 + 1][s * 2 + 1] += W * (eta * F_vv + eta_v * F_v);
112 }
113 }
114 } // end of the loop over q
115
116}
117
118/*!
119 * Compute the Jacobian contribution of the basal boundary condition.
120 *
121 * This method implements basal sliding.
122 */
124 const double *tauc_nodal,
125 const double *f_nodal,
126 const Vector2d *u_nodal,
127 double K[16][16]) {
128 int Nk = fem::q13d::n_chi;
129
130 Vector2d *u = m_work2[0];
131
132 double
133 *tauc = m_work[0],
134 *floatation = m_work[1];
135
136 face.evaluate(u_nodal, u);
137 face.evaluate(tauc_nodal, tauc);
138 face.evaluate(f_nodal, floatation);
139
140 for (unsigned int q = 0; q < face.n_pts(); ++q) {
141 auto W = face.weight(q) / m_scaling;
142
143 bool grounded = floatation[q] <= 0.0;
144 double beta = 0.0, dbeta = 0.0;
145 if (grounded) {
146 m_basal_sliding_law->drag_with_derivative(tauc[q], u[q].u, u[q].v, &beta, &dbeta);
147 }
148
149 // loop over all test functions
150 for (int t = 0; t < Nk; ++t) {
151 auto psi = face.chi(q, t);
152 for (int s = 0; s < Nk; ++s) {
153 auto phi = face.chi(q, s);
154
155 double p = psi * phi;
156
157 K[t * 2 + 0][s * 2 + 0] += W * p * (beta + dbeta * u[q].u * u[q].u);
158 K[t * 2 + 0][s * 2 + 1] += W * p * dbeta * u[q].u * u[q].v;
159 K[t * 2 + 1][s * 2 + 0] += W * p * dbeta * u[q].v * u[q].u;
160 K[t * 2 + 1][s * 2 + 1] += W * p * (beta + dbeta * u[q].v * u[q].v);
161 }
162 }
163 }
164}
165
166/*!
167 * Set the Jacobian to identity at Dirichlet nodes.
168 */
169void Blatter::jacobian_dirichlet(const DMDALocalInfo &info, Parameters **P, Mat J) {
170 PetscErrorCode ierr;
171
172 // Dirichlet scaling
173 Vector2d scaling = {1.0, 1.0};
174
175 // take care of Dirichlet nodes (both explicit and grid points outside the domain)
176 //
177 // here we loop over all the *owned* nodes
178 for (int j = info.ys; j < info.ys + info.ym; j++) {
179 for (int i = info.xs; i < info.xs + info.xm; i++) {
180 for (int k = info.zs; k < info.zs + info.zm; k++) {
181 if ((int)P[j][i].node_type == NODE_EXTERIOR or dirichlet_node(info, {i, j, k})) {
182
183 double identity[4] = {scaling.u, 0, 0, scaling.v};
184
185 MatStencil row;
186 row.i = k; // STORAGE_ORDER
187 row.j = i; // STORAGE_ORDER
188 row.k = j; // STORAGE_ORDER
189 ierr = MatSetValuesBlockedStencil(J, 1, &row, 1, &row, identity, ADD_VALUES);
190 PISM_CHK(ierr, "MatSetValuesBlockedStencil"); // this may throw
191 }
192 }
193 }
194 }
195}
196
197/*!
198 * Compute the Jacobian matrix.
199 */
200void Blatter::compute_jacobian(DMDALocalInfo *petsc_info,
201 const Vector2d ***X, Mat A, Mat J) {
202 auto info = grid_transpose(*petsc_info);
203
204 // Zero out the Jacobian in preparation for updating it.
205 PetscErrorCode ierr = MatZeroEntries(J);
206 PISM_CHK(ierr, "MatZeroEntries");
207
208 ierr = MatSetOption(A, MAT_SUBSET_OFF_PROC_ENTRIES, PETSC_TRUE);
209 PISM_CHK(ierr, "MatSetOption");
210
211 ierr = MatSetOption(J, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE);
212 PISM_CHK(ierr, "MatSetOption");
213
214 ierr = MatSetOption(J, MAT_SYMMETRIC, PETSC_TRUE);
215 PISM_CHK(ierr, "MatSetOption");
216
217 ierr = PetscObjectSetName((PetscObject)J, "bp_jacobian");
218 PISM_CHK(ierr, "PetscObjectSetName");
219
220 // Stencil width of 1 is not very important, but if info.sw > 1 will lead to more
221 // redundant computation (we would be looping over elements that don't contribute to any
222 // owned nodes).
223 assert(info.sw == 1);
224
225 // horizontal grid spacing is the same on all multigrid levels
226 double
227 x_min = m_grid->x0() - m_grid->Lx(),
228 y_min = m_grid->y0() - m_grid->Ly(),
229 dx = m_grid->dx(),
230 dy = m_grid->dy();
231
232 fem::Q1Element3 element(info,
234 dx, dy, x_min, y_min);
235
236 // Maximum number of nodes per element
237 const int Nk = fem::q13d::n_chi;
238 assert(element.n_chi() <= Nk);
239 assert(element.n_pts() <= m_Nq);
240
241 // scalar quantities
242 double z[Nk];
243 double floatation[Nk], bottom_elevation[Nk], ice_thickness[Nk];
244 double B_nodal[Nk], basal_yield_stress[Nk];
245 int node_type[Nk];
246
247 // 2D vector quantities
248 Vector2d velocity[Nk];
249
250 // note: we use info.da below because ice hardness is on the grid corresponding to the
251 // current multigrid level
252 //
253 // FIXME: This communicates ghosts of ice hardness
254 DataAccess<double***> hardness(info.da, 3, GHOSTED);
255
257 auto *P = m_parameters.array();
258
259 // loop over all the elements that have at least one owned node
260 for (int j = info.gys; j < info.gys + info.gym - 1; j++) {
261 for (int i = info.gxs; i < info.gxs + info.gxm - 1; i++) {
262
263 // Initialize 2D geometric info at element nodes
264 nodal_parameter_values(element, P, i, j,
265 node_type,
266 bottom_elevation,
267 ice_thickness,
268 NULL,
269 NULL);
270
271 // skip ice-free (exterior) columns
272 if (exterior_element(node_type)) {
273 continue;
274 }
275
276 for (int k = info.gzs; k < info.gzs + info.gzm - 1; k++) {
277
278 // Element-local Jacobian matrix (there are Nk vector valued degrees of freedom
279 // per element, for a total of Nk*Nk = 64 entries in the local Jacobian.
280 double K[2*Nk][2*Nk];
281 memset(K, 0, sizeof(K));
282
283 // Compute coordinates of the nodes of this element and fetch node types.
284 for (int n = 0; n < Nk; ++n) {
285 auto I = element.local_to_global(i, j, k, n);
286
287 z[n] = grid_z(bottom_elevation[n], ice_thickness[n], info.mz, I.k);
288 }
289
290 // compute values of chi, chi_x, chi_y, chi_z and quadrature weights at quadrature
291 // points on this physical element
292 element.reset(i, j, k, z);
293
294 // Get nodal values of ice velocity.
295 {
296 element.nodal_values(X, velocity);
297
298 // Don't contribute to Dirichlet nodes
299 for (int n = 0; n < Nk; ++n) {
300 auto I = element.local_to_global(n);
301 if (dirichlet_node(info, I)) {
302 element.mark_row_invalid(n);
303 element.mark_col_invalid(n);
304 velocity[n] = u_bc(element.x(n), element.y(n), element.z(n));
305 }
306 }
307 }
308
309 element.nodal_values((double***)hardness, B_nodal);
310
311 jacobian_f(element, velocity, B_nodal, K);
312
313 // basal boundary
314 if (k == 0) {
315 for (int n = 0; n < Nk; ++n) {
316 auto I = element.local_to_global(n);
317
318 basal_yield_stress[n] = P[I.j][I.i].tauc;
319 floatation[n] = P[I.j][I.i].floatation;
320 }
321
322 fem::Q1Element3Face *face = grounding_line(floatation) ? &m_face100 : &m_face4;
323
325
326 jacobian_basal(*face, basal_yield_stress, floatation, velocity, K);
327 }
328
329 // fill the lower-triangular part of the element Jacobian using the fact that J is
330 // symmetric
331 for (int t = 0; t < Nk; ++t) {
332 for (int s = 0; s < t; ++s) {
333 K[t * 2 + 0][s * 2 + 0] = K[s * 2 + 0][t * 2 + 0];
334 K[t * 2 + 1][s * 2 + 0] = K[s * 2 + 0][t * 2 + 1];
335 K[t * 2 + 0][s * 2 + 1] = K[s * 2 + 1][t * 2 + 0];
336 K[t * 2 + 1][s * 2 + 1] = K[s * 2 + 1][t * 2 + 1];
337 }
338 }
339
340 element.add_contribution(&K[0][0], J);
341 } // end of the loop over i
342 } // end of the loop over j
343 } // end of the loop over k
344
345 jacobian_dirichlet(info, P, J);
346
347 ierr = MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY); PISM_CHK(ierr, "MatAssemblyBegin");
348 ierr = MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY); PISM_CHK(ierr, "MatAssemblyEnd");
349 if (A != J) {
350 ierr = MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY); PISM_CHK(ierr, "MatAssemblyBegin");
351 ierr = MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY); PISM_CHK(ierr, "MatAssemblyEnd");
352 }
353}
354
355PetscErrorCode Blatter::jacobian_callback(DMDALocalInfo *info,
356 const Vector2d ***x,
357 Mat A, Mat J,
358 Blatter *solver) {
359 try {
360 solver->compute_jacobian(info, x, A, J);
361 } catch (...) {
362 MPI_Comm com = solver->grid()->com;
364 SETERRQ(com, 1, "A PISM callback failed");
365 }
366 return 0;
367}
368
369} // end of namespace stressbalance
370} // 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 void drag_with_derivative(double tauc, double vx, double vy, double *drag, double *ddrag) const
Compute the drag coefficient and its derivative with respect to .
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
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
void mark_col_invalid(unsigned int k)
Mark that the column corresponding to local degree of freedom k should not be updated when inserting ...
Definition Element.cc:227
3D Q1 element
Definition Element.hh:358
void jacobian_dirichlet(const DMDALocalInfo &info, Parameters **P, Mat J)
Definition jacobian.cc:169
virtual Vector2d u_bc(double x, double y, double z) const
Definition Blatter.cc:133
fem::Q1Element3Face m_face4
Definition Blatter.hh:113
double m_work[m_n_work][m_Nq]
Definition Blatter.hh:109
static PetscErrorCode jacobian_callback(DMDALocalInfo *info, const Vector2d ***x, Mat A, Mat J, Blatter *solver)
Definition jacobian.cc:355
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
virtual void jacobian_f(const fem::Q1Element3 &element, const Vector2d *u_nodal, const double *B_nodal, double K[2 *fem::q13d::n_chi][2 *fem::q13d::n_chi])
Definition jacobian.cc:36
static bool grounding_line(const double *F)
Definition Blatter.cc:167
virtual void jacobian_basal(const fem::Q1Element3Face &face, const double *tauc_nodal, const double *f_nodal, const Vector2d *u_nodal, double K[2 *fem::q13d::n_chi][2 *fem::q13d::n_chi])
Definition jacobian.cc:123
void compute_jacobian(DMDALocalInfo *info, const Vector2d ***x, Mat A, Mat J)
Definition jacobian.cc:200
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
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 PISM_CHK(errcode, name)
const double phi
Definition exactTestK.c:41
#define n
Definition exactTestM.c:37
const int n_chi
Number of shape functions on a Q1 element.
Definition FEM.hh:213
double grid_z(double b, double H, int Mz, int k)
@ 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)