ASPECT
stokes_matrix_free.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 #ifndef _aspect_simulator_solver_stokes_matrix_free_h
22 #define _aspect_simulator_solver_stokes_matrix_free_h
23 
25 #include <aspect/global.h>
26 #include <aspect/parameters.h>
28 #include <aspect/utilities.h>
29 
30 #include <deal.II/multigrid/mg_transfer_matrix_free.h>
31 #include <deal.II/multigrid/mg_transfer_global_coarsening.templates.h>
32 
33 #include <deal.II/lac/solver_bicgstab.h>
34 
35 namespace aspect
36 {
40  using GMGNumberType = double;
41 
46  template <int dim>
48  {
49  public:
54  virtual void setup_dofs()=0;
55 
63  virtual void assemble()=0;
64 
70  virtual void build_preconditioner()=0;
71 
75  static
76  void declare_parameters (ParameterHandler &prm);
77 
81  virtual std::size_t get_dof_handler_memory_consumption() const = 0;
82 
86  virtual std::size_t get_mg_transfer_memory_consumption() const = 0;
87 
91  virtual std::size_t get_constraint_memory_consumption() const = 0;
92 
97  virtual std::size_t get_cell_data_memory_consumption() const = 0;
98  };
99 
103  template <int dim>
104  std::unique_ptr<StokesMatrixFreeHandler<dim>> create_matrix_free_solver(Simulator<dim> &simulator, const Parameters<dim> &parameters);
105 
106 
107  namespace internal
108  {
109 
113  template <class StokesMatrixType, class ABlockMatrixType, class BTBlockOperatorType,
114  class SchurComplementMatrixType, class ABlockPreconditionerType,
115  class SchurComplementPreconditionerType>
117 #if DEAL_II_VERSION_GTE(9,7,0)
118  EnableObserverPointer
119 #else
121 #endif
122  {
123  public:
143  BlockSchurGMGPreconditioner (const StokesMatrixType &Stokes_matrix,
144  const ABlockMatrixType &A_block,
145  const BTBlockOperatorType &BT_block,
146  const SchurComplementMatrixType &Schur_complement_block,
147  const ABlockPreconditionerType &A_block_preconditioner,
148  const SchurComplementPreconditionerType &Schur_complement_preconditioner,
149  const bool do_solve_A,
150  const bool do_solve_Schur_complement,
151  const bool A_block_is_symmetric,
152  const double A_block_tolerance,
153  const double Schur_complement_tolerance);
154 
158  void vmult (::LinearAlgebra::distributed::BlockVector<double> &dst,
159  const ::LinearAlgebra::distributed::BlockVector<double> &src) const;
160 
161  unsigned int n_iterations_A_block() const;
162  unsigned int n_iterations_Schur_complement() const;
163 
164 
165  private:
169  const StokesMatrixType &stokes_matrix;
170  const ABlockMatrixType &A_block;
171  const BTBlockOperatorType &BT_block;
172  const SchurComplementMatrixType &Schur_complement_block;
173  const ABlockPreconditionerType &A_block_preconditioner;
174  const SchurComplementPreconditionerType &Schur_complement_preconditioner;
175 
180  const bool do_solve_A;
183  mutable unsigned int n_iterations_A_;
184  mutable unsigned int n_iterations_Schur_complement_;
185  const double A_block_tolerance;
187  mutable ::LinearAlgebra::distributed::BlockVector<double> utmp;
188  };
189 
190  template <class StokesMatrixType, class ABlockMatrixType, class BTBlockOperatorType, class SchurComplementMatrixType,
191  class ABlockPreconditionerType, class SchurComplementPreconditionerType>
192  BlockSchurGMGPreconditioner<StokesMatrixType, ABlockMatrixType, BTBlockOperatorType, SchurComplementMatrixType,
193  ABlockPreconditionerType, SchurComplementPreconditionerType>::
194  BlockSchurGMGPreconditioner (const StokesMatrixType &Stokes_matrix,
195  const ABlockMatrixType &A_block,
196  const BTBlockOperatorType &BT_block,
197  const SchurComplementMatrixType &Schur_complement_block,
198  const ABlockPreconditionerType &A_block_preconditioner,
199  const SchurComplementPreconditionerType &Schur_complement_preconditioner,
200  const bool do_solve_A,
201  const bool do_solve_Schur_complement,
202  const bool A_block_symmetric,
203  const double A_block_tolerance,
204  const double Schur_complement_tolerance)
205  :
206  stokes_matrix (Stokes_matrix),
207  A_block (A_block),
208  BT_block (BT_block),
209  Schur_complement_block (Schur_complement_block),
210  A_block_preconditioner (A_block_preconditioner),
211  Schur_complement_preconditioner (Schur_complement_preconditioner),
212  do_solve_A (do_solve_A),
213  do_solve_Schur_complement (do_solve_Schur_complement),
214  A_block_is_symmetric (A_block_symmetric),
215  n_iterations_A_ (0),
216  n_iterations_Schur_complement_ (0),
217  A_block_tolerance (A_block_tolerance),
218  Schur_complement_tolerance (Schur_complement_tolerance)
219  {}
220 
221  template <class StokesMatrixType, class ABlockMatrixType, class BTBlockOperatorType,
222  class SchurComplementMatrixType, class ABlockPreconditionerType,
223  class SchurComplementPreconditionerType>
224  unsigned int
225  BlockSchurGMGPreconditioner<StokesMatrixType, ABlockMatrixType, BTBlockOperatorType, SchurComplementMatrixType,
226  ABlockPreconditionerType, SchurComplementPreconditionerType>::
228  {
229  return n_iterations_A_;
230  }
231 
232  template <class StokesMatrixType, class ABlockMatrixType, class BTBlockOperatorType, class SchurComplementMatrixType,
233  class ABlockPreconditionerType, class SchurComplementPreconditionerType>
234  unsigned int
235  BlockSchurGMGPreconditioner<StokesMatrixType, ABlockMatrixType, BTBlockOperatorType, SchurComplementMatrixType,
236  ABlockPreconditionerType, SchurComplementPreconditionerType>::
238  {
240  }
241 
242  template <class StokesMatrixType, class ABlockMatrixType, class BTBlockOperatorType, class SchurComplementMatrixType,
243  class ABlockPreconditionerType, class SchurComplementPreconditionerType>
244  void
245  BlockSchurGMGPreconditioner<StokesMatrixType, ABlockMatrixType, BTBlockOperatorType, SchurComplementMatrixType,
246  ABlockPreconditionerType, SchurComplementPreconditionerType>::
247  vmult (::LinearAlgebra::distributed::BlockVector<double> &dst,
248  const ::LinearAlgebra::distributed::BlockVector<double> &src) const
249  {
250  if (utmp.size()==0)
251  utmp.reinit(src);
252 
253  // This needs to be done explicitly, as GMRES does not
254  // initialize the data of the vector dst before calling
255  // us. Otherwise we might use random data as our initial guess.
256  dst = 0.0;
257 
258  // either solve with the Schur complement matrix (if do_solve_Schur_complement==true)
259  // or just apply one preconditioner sweep (for the first few
260  // iterations of our two-stage outer GMRES iteration)
262  {
263  // first solve with the bottom right block, which we have built
264  // as a mass matrix with the inverse of the viscosity
265  SolverControl solver_control(100, src.block(1).l2_norm() * Schur_complement_tolerance,true);
266 
267  SolverCG<::LinearAlgebra::distributed::Vector<double>> solver(solver_control);
268  // Trilinos reports a breakdown
269  // in case src=dst=0, even
270  // though it should return
271  // convergence without
272  // iterating. We simply skip
273  // solving in this case.
274  if (src.block(1).l2_norm() > 1e-50)
275  {
276  try
277  {
278  solver.solve(Schur_complement_block,
279  dst.block(1), src.block(1),
281  n_iterations_Schur_complement_ += solver_control.last_step();
282  }
283  // if the solver fails, report the error from processor 0 with some additional
284  // information about its location, and throw a quiet exception on all other
285  // processors
286  catch (const std::exception &exc)
287  {
288  Utilities::throw_linear_solver_failure_exception("iterative (bottom right) solver",
289  "BlockSchurGMGPreconditioner::vmult",
290  std::vector<SolverControl> {solver_control},
291  exc,
292  src.block(0).get_mpi_communicator());
293  }
294  }
295  }
296  else
297  {
298  Schur_complement_preconditioner.vmult(dst.block(1),src.block(1));
300  }
301 
302  dst.block(1) *= -1.0;
303 
304  {
305  BT_block.vmult(utmp,dst);
306  utmp.block(0) *= -1.0;
307  utmp.block(0) += src.block(0);
308  }
309 
310  // now either solve with the top left block (if do_solve_A==true)
311  // or just apply one preconditioner sweep (for the first few
312  // iterations of our two-stage outer GMRES iteration)
313  if (do_solve_A == true)
314  {
315  SolverControl solver_control(1000, utmp.block(0).l2_norm() * A_block_tolerance);
316  PrimitiveVectorMemory<::LinearAlgebra::distributed::Vector<double>> mem;
317 
318  try
319  {
321  {
322  SolverCG<::LinearAlgebra::distributed::Vector<double>> solver(solver_control,mem);
323  solver.solve(A_block, dst.block(0), utmp.block(0),
325  }
326  else
327  {
328  // Use BiCGStab for non-symmetric matrices.
329  // BiCGStab can also solve indefinite systems if necessary.
330  // Do not compute the exact residual, as this
331  // is more expensive, and we only need an approximate solution.
332  SolverBicgstab<::LinearAlgebra::distributed::Vector<double>>
333  solver(solver_control,
334  mem,
335  SolverBicgstab<::LinearAlgebra::distributed::Vector<double>>::AdditionalData(/*exact_residual=*/ false));
336  solver.solve(A_block, dst.block(0), utmp.block(0),
338  }
339 
340  n_iterations_A_ += solver_control.last_step();
341  }
342  // if the solver fails, report the error from processor 0 with some additional
343  // information about its location, and throw a quiet exception on all other
344  // processors
345  catch (const std::exception &exc)
346  {
347  Utilities::throw_linear_solver_failure_exception("iterative (top left) solver",
348  "BlockSchurGMGPreconditioner::vmult",
349  std::vector<SolverControl> {solver_control},
350  exc,
351  src.block(0).get_mpi_communicator());
352  }
353  }
354  else
355  {
356  A_block_preconditioner.vmult (dst.block(0), utmp.block(0));
357  n_iterations_A_ += 1;
358  }
359  }
360  }
361 }
362 
363 #endif
virtual std::size_t get_dof_handler_memory_consumption() const =0
const SchurComplementMatrixType & Schur_complement_block
const Simulator< dim > * simulator
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="")
const SchurComplementPreconditionerType & Schur_complement_preconditioner
virtual std::size_t get_mg_transfer_memory_consumption() const =0
void vmult(::LinearAlgebra::distributed::BlockVector< double > &dst, const ::LinearAlgebra::distributed::BlockVector< double > &src) const
virtual std::size_t get_cell_data_memory_consumption() const =0
std::unique_ptr< StokesMatrixFreeHandler< dim > > create_matrix_free_solver(Simulator< dim > &simulator, const Parameters< dim > &parameters)
double GMGNumberType
mutable ::LinearAlgebra::distributed::BlockVector< double > utmp
virtual void build_preconditioner()=0
const ABlockPreconditionerType & A_block_preconditioner
static void declare_parameters(ParameterHandler &prm)
virtual std::size_t get_constraint_memory_consumption() const =0