PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
Poisson.cc
Go to the documentation of this file.
1 /* Copyright (C) 2019, 2020, 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 "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/interpolation.hh"
26 
27 namespace pism {
28 
29 Poisson::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  */
67 int 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)$
159 void 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 
267 void 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:65
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:179
petsc::Vec & vec() const
Definition: Array.cc:339
std::shared_ptr< petsc::DM > dm() const
Definition: Array.cc:353
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 Vector3 row(const double A[3][3], size_t k)
Definition: Element.cc:46
const int J[]
Definition: ssafd_code.cc:34
const int I[]
Definition: ssafd_code.cc:24
static double S(unsigned n)
Definition: test_cube.c:58