ASPECT
block_stokes_preconditioner.h
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2025 by the authors of the ASPECT code.
3 
4  This file is part of ASPECT.
5 
6  ASPECT is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2, or (at your option)
9  any later version.
10 
11  ASPECT is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with ASPECT; see the file LICENSE. If not see
18  <http://www.gnu.org/licenses/>.
19 */
20 
21 
22 #ifndef aspect_block_stokes_preconditioner_h
23 #define aspect_block_stokes_preconditioner_h
24 
25 #include <deal.II/lac/solver_bicgstab.h>
26 #include <deal.II/lac/solver_cg.h>
27 
28 
29 #include <deal.II/lac/la_parallel_vector.h>
30 #include <deal.II/lac/la_parallel_block_vector.h>
31 
32 namespace aspect
33 {
34 
35  namespace internal
36  {
43  template <class PreconditionerA, class VectorType, class ABlockType>
45  {
46  public:
57  InverseVelocityBlock(const ABlockType &matrix,
58  const PreconditionerA &preconditioner,
59  const bool do_solve_A,
60  const bool A_block_is_symmetric,
61  const double solver_tolerance);
62 
63  void vmult(VectorType &dst,
64  const VectorType &src) const;
65 
66  unsigned int n_iterations() const;
67 
68  private:
69  mutable unsigned int n_iterations_;
70  const ABlockType &matrix;
71  const PreconditionerA &preconditioner;
72  const bool do_solve_A;
74  const double solver_tolerance;
75  };
76 
77 
78 
79  template <class PreconditionerA,class VectorType, class ABlockType>
81  const ABlockType &matrix,
82  const PreconditionerA &preconditioner,
83  const bool do_solve_A,
84  const bool A_block_is_symmetric,
85  const double solver_tolerance)
86  : n_iterations_ (0),
87  matrix (matrix),
88  preconditioner (preconditioner),
89  do_solve_A (do_solve_A),
90  A_block_is_symmetric (A_block_is_symmetric),
91  solver_tolerance (solver_tolerance)
92  {}
93 
94 
95 
100  template <class PreconditionerA, class VectorType, class ABlockType>
102  const VectorType &src) const
103  {
104 
105  // Either solve with the top left block
106  // or just apply one preconditioner sweep (for the first few
107  // iterations of our two-stage outer GMRES iteration)
108  if (do_solve_A == true)
109  {
110  SolverControl solver_control(10000, src.l2_norm() * solver_tolerance);
111  PrimitiveVectorMemory<VectorType> mem;
112 
113  try
114  {
115  dst = 0.0;
116 
118  {
119  SolverCG<VectorType> solver(solver_control, mem);
120  solver.solve(matrix, dst, src, preconditioner);
121  }
122  else
123  {
124  // Use BiCGStab for non-symmetric matrices.
125  // BiCGStab can also solve indefinite systems if necessary.
126  // Do not compute the exact residual, as this
127  // is more expensive, and we only need an approximate solution.
128  SolverBicgstab<VectorType>
129  solver(solver_control,
130  mem,
131  typename SolverBicgstab<VectorType>::AdditionalData(/*exact_residual=*/ false));
132  solver.solve(matrix, dst, src, preconditioner);
133  }
134  n_iterations_ += solver_control.last_step();
135  }
136  catch (const std::exception &exc)
137  {
138  // if the solver fails, report the error from processor 0 with some additional
139  // information about its location, and throw a quiet exception on all other
140  // processors
141  Utilities::throw_linear_solver_failure_exception("iterative (top left) solver",
142  "BlockSchurPreconditioner::vmult",
143  std::vector<SolverControl> {solver_control},
144  exc,
145  src.get_mpi_communicator());
146  }
147  }
148  else
149  {
150  preconditioner.vmult (dst, src);
151  n_iterations_ += 1;
152  }
153  }
154 
155 
156 
157  template <class PreconditionerA, class VectorType, class ABlockType>
159  {
160  return n_iterations_;
161  }
162 
167  template <class AInvOperator, class SInvOperator, class BTOperator, class VectorType>
169 #if DEAL_II_VERSION_GTE(9,7,0)
170  EnableObserverPointer
171 #else
173 #endif
174 
175  {
176  public:
184  const AInvOperator &A_inverse_operator,
185  const SInvOperator &S_inverse_operator,
186  const BTOperator &BT_operator);
187 
191  void vmult (VectorType &dst,
192  const VectorType &src) const;
193 
194  private:
199  const AInvOperator &A_inverse_operator;
200  const SInvOperator &S_inverse_operator;
201  const BTOperator &BT_operator;
202  mutable VectorType tmp;
203  };
204 
205 
206  template <class AInvOperator, class SInvOperator, class BTOperator, class VectorType>
209  const AInvOperator &A_inverse_operator,
210  const SInvOperator &S_inverse_operator,
211  const BTOperator &BT_operator)
212  :
213  A_inverse_operator (A_inverse_operator),
214  S_inverse_operator (S_inverse_operator),
215  BT_operator (BT_operator)
216  {}
217 
218 
219 
220  template <class AInvOperator, class SInvOperator, class BTOperator, class VectorType>
221  void
223  vmult (VectorType &dst,
224  const VectorType &src) const
225  {
226  if (tmp.size() == 0)
227  tmp.reinit(src);
228 
229 
230  dst=0.0;
231 
232  // first apply the Schur Complement inverse operator.
233  {
234  S_inverse_operator.vmult(dst.block(1),src.block(1));
235  dst.block(1) *= -1.0;
236  }
237 
238 
239  // apply the top right block: B^T or J^{up}
240  {
241  // For matrix-free usage, the BT operator needs to operate on the whole vector,
242  // but the matrix-based version is a SparseMatrix::vmult() that requires passing the
243  // individual blocks.
244  if constexpr (std::is_same_v<VectorType, ::LinearAlgebra::distributed::BlockVector<double>>)
245  BT_operator.vmult(tmp, dst);
246 
247  else
248  BT_operator.vmult(tmp.block(0), dst.block(1));
249 
250  tmp.block(0) *= -1.0;
251  tmp.block(0) += src.block(0);
252  }
253 
254  A_inverse_operator.vmult(dst.block(0), tmp.block(0));
255  }
256 
257 
258 
259  template<class OperatorType, class StokesMatrixType, class SchurComplementMatrixType, class VectorType>
261  {
262  public:
271  SchurApproximation(const OperatorType &schur_preconditioner,
272  const StokesMatrixType &stokes_matrix,
273  const SchurComplementMatrixType &Schur_complement_block,
274  const bool do_solve_Schur_complement,
275  const double Schur_complement_tolerance);
276  void vmult( VectorType &dst, const VectorType &src) const;
277  unsigned int n_iterations() const;
278 
279  private:
280  const OperatorType &schur_preconditioner;
281  const StokesMatrixType &stokes_matrix;
282  const SchurComplementMatrixType &Schur_complement_block;
285  mutable unsigned int n_iterations_Schur_complement_;
286 
287 
288  };
289 
290 
291 
292  template <class OperatorType, class StokesMatrixType, class SchurComplementMatrixType, class VectorType>
294  const StokesMatrixType &stokes_matrix,
295  const SchurComplementMatrixType &Schur_complement_block,
296  const bool do_solve_Schur_complement,
297  const double Schur_complement_tolerance)
298  :
299  schur_preconditioner(schur_preconditioner),
300  stokes_matrix(stokes_matrix),
301  Schur_complement_block(Schur_complement_block),
302  do_solve_Schur_complement(do_solve_Schur_complement),
303  Schur_complement_tolerance(Schur_complement_tolerance),
304  n_iterations_Schur_complement_(0)
305  {}
306 
307 
308  template<class OperatorType, class StokesMatrixType, class SchurComplementMatrixType, class VectorType>
310  const VectorType &src) const
311  {
313  {
314 
315  SolverControl solver_control(100, src.l2_norm() * Schur_complement_tolerance,true);
316 
317  SolverCG<VectorType> solver(solver_control);
318  // Trilinos reports a breakdown in case src=dst=0, even
319  // though it should return convergence without
320  // iterating. We simply skip solving in this case.
321  if (src.l2_norm() > 1e-50)
322  {
323  try
324  {
325  // explicitly zero out because GMRES does not guarantee that dst is zeroed out
326  dst = 0.0;
327 
328  solver.solve(Schur_complement_block,
329  dst, src,
331  n_iterations_Schur_complement_ += solver_control.last_step();
332  }
333  // if the solver fails, report the error from processor 0 with some additional
334  // information about its location, and throw a quiet exception on all other
335  // processors
336  catch (const std::exception &exc)
337  {
338  Utilities::throw_linear_solver_failure_exception("iterative (bottom right) solver",
339  "BlockSchurPreconditioner::vmult",
340  std::vector<SolverControl> {solver_control},
341  exc,
342  src.get_mpi_communicator());
343  }
344  }
345  }
346  else
347  {
348  dst = 0.0;
349  schur_preconditioner.vmult(dst,src);
351  }
352  }
353  template<class OperatorType, class StokesMatrixType, class SchurComplementMatrixType, class VectorType>
355  {
357  }
358 
359 
360  }
361 }
362 
363 
364 
365 #endif
InverseVelocityBlock(const ABlockType &matrix, const PreconditionerA &preconditioner, const bool do_solve_A, const bool A_block_is_symmetric, const double solver_tolerance)
void throw_linear_solver_failure_exception(const std::string &solver_name, const std::string &function_name, const std::vector< SolverControl > &solver_controls, const std::exception &exc, const MPI_Comm mpi_communicator, const std::string &output_filename="")
void vmult(VectorType &dst, const VectorType &src) const
SchurApproximation(const OperatorType &schur_preconditioner, const StokesMatrixType &stokes_matrix, const SchurComplementMatrixType &Schur_complement_block, const bool do_solve_Schur_complement, const double Schur_complement_tolerance)
Constructor.
BlockSchurPreconditioner(const AInvOperator &A_inverse_operator, const SInvOperator &S_inverse_operator, const BTOperator &BT_operator)
Constructor.
const SchurComplementMatrixType & Schur_complement_block
void vmult(VectorType &dst, const VectorType &src) const
void vmult(VectorType &dst, const VectorType &src) const