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
Poisson.cc
Go to the documentation of this file.
1/* Copyright (C) 2019, 2020, 2022, 2023, 2024 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/util/Poisson.hh"
21#include "pism/util/error_handling.hh"
22#include "pism/util/Context.hh"
23#include "pism/util/petscwrappers/DM.hh"
24#include "pism/util/petscwrappers/Vec.hh"
25#include "pism/util/Interpolation1D.hh"
26
27namespace pism {
28
29Poisson::Poisson(std::shared_ptr<const Grid> grid)
30 : m_grid(grid),
31 m_log(grid->ctx()->log()),
32 m_b(grid, "poisson_rhs"),
33 m_x(grid, "poisson_x"),
34 m_mask(grid, "poisson_mask") {
35
36 m_da = m_x.dm();
38
39 // PETSc objects and settings
40 {
41 PetscErrorCode ierr;
42 ierr = DMSetMatType(*m_da, MATAIJ);
43 PISM_CHK(ierr, "DMSetMatType");
44
45 ierr = DMCreateMatrix(*m_da, m_A.rawptr());
46 PISM_CHK(ierr, "DMCreateMatrix");
47
48 ierr = KSPCreate(m_grid->com, m_KSP.rawptr());
49 PISM_CHK(ierr, "KSPCreate");
50
51 ierr = KSPSetOptionsPrefix(m_KSP, "poisson_");
52 PISM_CHK(ierr, "KSPSetOptionsPrefix");
53
54 // Process options:
55 ierr = KSPSetFromOptions(m_KSP);
56 PISM_CHK(ierr, "KSPSetFromOptions");
57 }
58}
59
60/*!
61 * Solve the Poisson equation on the domain defined by `mask == 1` with Dirichlet BC
62 * provided in `bc` (used only where `mask == 0`, possibly redundant away from the domain)
63 * with the constant right hand side `rhs`.
64 *
65 * Set the mask to 2 to use zero Neumann BC.
66 */
67int Poisson::solve(const array::Scalar& mask, const array::Scalar& bc, double rhs,
68 bool reuse_matrix) {
69
70 PetscErrorCode ierr;
71
72 // make a ghosted copy of the mask
73 m_mask.copy_from(mask);
74
75 if (reuse_matrix) {
76 // Use non-zero initial guess. I assume that re-using the matrix means that the BC and
77 // RHS provided are close to the ones used in the previous call and the solution
78 // stored in m_x is a good initial guess for the current problem.
79 ierr = KSPSetInitialGuessNonzero(m_KSP, PETSC_TRUE);
80 PISM_CHK(ierr, "KSPSetInitialGuessNonzero");
81 } else {
82 ierr = KSPSetInitialGuessNonzero(m_KSP, PETSC_FALSE);
83 PISM_CHK(ierr, "KSPSetInitialGuessNonzero");
84
86 }
87
88 assemble_rhs(rhs, m_mask, bc, m_b);
89
90 // Call PETSc to solve linear system by iterative method.
91 ierr = KSPSetOperators(m_KSP, m_A, m_A);
92 PISM_CHK(ierr, "KSPSetOperator");
93
94 ierr = KSPSolve(m_KSP, m_b.vec(), m_x.vec());
95 PISM_CHK(ierr, "KSPSolve");
96
97 // Check if diverged
98 KSPConvergedReason reason;
99 ierr = KSPGetConvergedReason(m_KSP, &reason);
100 PISM_CHK(ierr, "KSPGetConvergedReason");
101
102 if (reason < 0) {
103 // KSP diverged
104 m_log->message(1,
105 "PISM ERROR: KSP iteration failed while solving the Poisson equation\n"
106 " reason = %d = '%s'\n",
107 reason, KSPConvergedReasons[reason]);
108
109 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "KSP iteration failed: %s",
110 KSPConvergedReasons[reason]);
111 }
112
113 // report on KSP success
114 PetscInt ksp_iterations = 0;
115 ierr = KSPGetIterationNumber(m_KSP, &ksp_iterations);
116 PISM_CHK(ierr, "KSPGetIterationNumber");
117
118 return ksp_iterations;
119}
120
122 return m_x;
123}
124
125// Maxima code deriving the discretization
126//
127// /* Shift in x and y directions. */
128// shift(expr, dx, dy) := op(expr)[args(expr)[1] + dx, args(expr)[2] + dy]$
129//
130// d_px(var) := (shift(var, 1, 0) - var)$
131// d_mx(var) := (var - shift(var, -1, 0))$
132//
133// d_py(var) := (shift(var, 0, 1) - var)$
134// d_my(var) := (var - shift(var, 0, -1))$
135//
136// constants : [C_x = 1 / (dx^2), C_y = 1 / (dy^2)]$
137//
138// /* discretization of -\nabla \dot (D \nabla W) */
139// L: (E * d_px(W[i, j]) - W * d_mx(W[i, j])) / dx^2 +
140// (N * d_py(W[i, j]) - S * d_my(W[i, j])) / dy^2$
141//
142// /* discretization of the Poisson equation */
143// eq: - L = f;
144//
145// /* cleaned up equation */
146// s : map(lambda([x,y], solve(x, y)[1]), constants, [dx^2, dy^2]);
147// eq2 : at(eq, s);
148//
149// /* compute matrix coefficients */
150// for m: -1 thru 1 do (for n: -1 thru 1 do
151// (c[2 - n, m + 2] : factor(ratcoef(lhs(eq2), W[i + m, j + n]))))$
152//
153// /* print results */
154// A : genmatrix(c, 3, 3);
155//
156// b : rhs(eq2);
157//
158// print(''out)$
159void Poisson::assemble_matrix(const array::Scalar1 &mask, Mat A) {
160 PetscErrorCode ierr = 0;
161
162 const double
163 dx = m_grid->dx(),
164 dy = m_grid->dy(),
165 C_x = 1.0 / (dx * dx),
166 C_y = 1.0 / (dy * dy);
167
168 const int
169 nrow = 1,
170 ncol = 5,
171 Mx = m_grid->Mx(),
172 My = m_grid->My();
173
174 ierr = MatZeroEntries(A); PISM_CHK(ierr, "MatZeroEntries");
175
176 array::AccessScope list{&mask};
177
178 /* matrix assembly loop */
179 ParallelSection loop(m_grid->com);
180 try {
181 MatStencil row, col[ncol];
182 row.c = 0;
183
184 for (int m = 0; m < ncol; m++) {
185 col[m].c = 0;
186 }
187
188 for (auto p = m_grid->points(); p; p.next()) {
189 const int i = p.i(), j = p.j();
190
191
192 /* Order of grid points in the stencil:
193 *
194 * 0
195 * 1 2 3
196 * 4
197 */
198
199 /* i indices */
200 const int I[] = {i, i - 1, i, i + 1, i};
201
202 /* j indices */
203 const int J[] = {j + 1, j, j, j, j - 1};
204
205 row.i = i;
206 row.j = j;
207
208 for (int m = 0; m < ncol; m++) {
209 col[m].i = I[m];
210 col[m].j = J[m];
211 }
212
213 auto M = mask.star(i, j);
214
215 if (M.c == 1) {
216 // Regular location: use coefficients of the discretization of the Laplacian
217
218 // Use zero Neumann BC if a neighbor is marked as a Neumann boundary
219 double
220 N = M.n == 2 ? 0.0 : 1.0,
221 E = M.e == 2 ? 0.0 : 1.0,
222 W = M.w == 2 ? 0.0 : 1.0,
223 S = M.s == 2 ? 0.0 : 1.0;
224
225 // Use zero Neumann BC at edges of the computational domain
226 {
227 N = j == My - 1 ? 0.0 : N;
228 E = i == Mx - 1 ? 0.0 : E;
229 W = i == 0 ? 0.0 : W;
230 S = j == 0 ? 0.0 : S;
231 }
232
233 // discretization of the Laplacian
234 double L[ncol] = {- N * C_y,
235 - W * C_x, (W + E) * C_x + (N + S) * C_y, - E * C_x,
236 - S * C_y};
237
238 ierr = MatSetValuesStencil(A, nrow, &row, ncol, col, L, INSERT_VALUES);
239 PISM_CHK(ierr, "MatSetValuesStencil");
240 } else {
241 // Boundary or outside the domain: assemble trivial equations (1 on the diagonal,
242 // 0 elsewhere)
243 double D[ncol] = {0.0,
244 0.0, 1.0, 0.0,
245 0.0};
246
247 ierr = MatSetValuesStencil(A, nrow, &row, ncol, col, D, INSERT_VALUES);
248 PISM_CHK(ierr, "MatSetValuesStencil");
249 }
250
251 } // i,j-loop
252 } catch (...) {
253 loop.failed();
254 }
255 loop.check();
256
257 ierr = MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY); PISM_CHK(ierr, "MatAssemblyBegin");
258 ierr = MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY); PISM_CHK(ierr, "MatAssemblyif");
259
260#if (Pism_DEBUG==1)
261 ierr = MatSetOption(A, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE);
262 PISM_CHK(ierr, "MatSetOption");
263#endif
264
265}
266
267void Poisson::assemble_rhs(double rhs,
268 const array::Scalar &mask,
269 const array::Scalar &bc,
270 array::Scalar &b) {
271 array::AccessScope list{&mask, &bc, &b};
272
273 for (auto p = m_grid->points(); p; p.next()) {
274 const int i = p.i(), j = p.j();
275
276 if (mask.as_int(i, j) == 1) {
277 // inside the domain
278 b(i, j) = rhs;
279 } else {
280 // at the boundary or outside the domain
281 b(i, j) = bc(i, j);
282 }
283 }
284}
285
286} // end of namespace pism
void failed()
Indicates a failure of a parallel section.
int solve(const array::Scalar &mask, const array::Scalar &bc, double rhs, bool reuse_matrix=false)
Definition Poisson.cc:67
void assemble_matrix(const array::Scalar1 &mask, Mat A)
Definition Poisson.cc:159
petsc::Mat m_A
Definition Poisson.hh:48
std::shared_ptr< const Grid > m_grid
Definition Poisson.hh:44
const array::Scalar & solution() const
Definition Poisson.cc:121
std::shared_ptr< petsc::DM > m_da
Definition Poisson.hh:46
void assemble_rhs(double rhs, const array::Scalar &mask, const array::Scalar &bc, array::Scalar &b)
Definition Poisson.cc:267
array::Scalar1 m_mask
Definition Poisson.hh:51
array::Scalar m_b
Definition Poisson.hh:49
petsc::KSP m_KSP
Definition Poisson.hh:47
Poisson(std::shared_ptr< const Grid > grid)
Definition Poisson.cc:29
array::Scalar m_x
Definition Poisson.hh:50
Logger::ConstPtr m_log
Definition Poisson.hh:45
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
T * rawptr()
Definition Wrapper.hh:39
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition Array.hh:64
void copy_from(const Array2D< T > &source)
Definition Array2D.hh:73
stencils::Star< T > star(int i, int j) const
Definition Array2D.hh:79
void set_interpolation_type(InterpolationType type)
Definition Array.cc:178
petsc::Vec & vec() const
Definition Array.cc:310
std::shared_ptr< petsc::DM > dm() const
Definition Array.cc:324
int as_int(int i, int j) const
Definition Scalar.hh:45
#define PISM_CHK(errcode, name)
#define PISM_ERROR_LOCATION
static const double L
Definition exactTestL.cc:40
static double S(unsigned n)
Definition test_cube.c:58