https://mooseframework.inl.gov
PhysicsBasedPreconditioner.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
11 
12 // MOOSE includes
14 #include "FEProblem.h"
15 #include "MooseEnum.h"
16 #include "MooseVariableFE.h"
17 #include "NonlinearSystem.h"
18 #include "PetscSupport.h"
19 
20 #include "libmesh/libmesh_common.h"
21 #include "libmesh/equation_systems.h"
22 #include "libmesh/nonlinear_implicit_system.h"
23 #include "libmesh/nonlinear_solver.h"
24 #include "libmesh/linear_implicit_system.h"
25 #include "libmesh/transient_system.h"
26 #include "libmesh/numeric_vector.h"
27 #include "libmesh/sparse_matrix.h"
28 #include "libmesh/string_to_enum.h"
29 #include "libmesh/coupling_matrix.h"
30 
31 using namespace libMesh;
32 
34 
37 {
39 
40  params.addClassDescription("Physics-based preconditioner (PBP) allows individual physics to have "
41  "their own preconditioner.");
42 
43  params.addRequiredParam<std::vector<std::string>>(
44  "solve_order",
45  "The order the block rows will be solved in. Put the name of variables here "
46  "to stand for solving that variable's block row. A variable may appear more "
47  "than once (to create cylces if you like).");
48  params.addRequiredParam<std::vector<std::string>>("preconditioner", "TODO: docstring");
49 
50  return params;
51 }
52 
54  : MoosePreconditioner(params),
55  Preconditioner<Number>(MoosePreconditioner::_communicator),
56  _nl(_fe_problem.getNonlinearSystemBase(_nl_sys_num))
57 {
58  const auto & libmesh_system = _nl.system();
59  unsigned int num_systems = _nl.system().n_vars();
60  _systems.resize(num_systems);
61  _preconditioners.resize(num_systems);
62  _off_diag.resize(num_systems);
63  _off_diag_mats.resize(num_systems);
64  _pre_type.resize(num_systems);
65 
66  { // Setup the Coupling Matrix so MOOSE knows what we're doing
67  unsigned int n_vars = _nl.nVariables();
68 
69  // The coupling matrix is held and released by FEProblemBase, so it is not released in this
70  // object
71  std::unique_ptr<CouplingMatrix> cm = std::make_unique<CouplingMatrix>(n_vars);
72 
73  bool full = getParam<bool>("full");
74 
75  if (!full)
76  {
77  // put 1s on diagonal
78  for (unsigned int i = 0; i < n_vars; i++)
79  (*cm)(i, i) = 1;
80 
81  // off-diagonal entries
82  for (const auto & off_diag : getParam<NonlinearVariableName, NonlinearVariableName>(
83  "off_diag_row", "off_diag_column"))
84  {
85  const auto row = libmesh_system.variable_number(off_diag.first);
86  const auto column = libmesh_system.variable_number(off_diag.second);
87  (*cm)(row, column) = 1;
88  }
89 
90  // TODO: handle coupling entries between NL-vars and SCALAR-vars
91  }
92  else
93  {
94  for (unsigned int i = 0; i < n_vars; i++)
95  for (unsigned int j = 0; j < n_vars; j++)
96  (*cm)(i, j) = 1;
97  }
98 
99  setCouplingMatrix(std::move(cm));
100  }
101 
102  // PC types
103  const std::vector<std::string> & pc_types = getParam<std::vector<std::string>>("preconditioner");
104  for (unsigned int i = 0; i < num_systems; i++)
105  _pre_type[i] = Utility::string_to_enum<PreconditionerType>(pc_types[i]);
106 
107  // solve order
108  const std::vector<std::string> & solve_order = getParam<std::vector<std::string>>("solve_order");
109  _solve_order.resize(solve_order.size());
110  for (const auto i : index_range(solve_order))
111  _solve_order[i] = _nl.system().variable_number(solve_order[i]);
112 
113  // diag and off-diag systems
114  unsigned int n_vars = _nl.system().n_vars();
115 
116  // off-diagonal entries
117  std::vector<std::vector<unsigned int>> off_diag(n_vars);
118  if (isParamValid("off_diag_row") && isParamValid("off_diag_column"))
119  {
120  const std::vector<NonlinearVariableName> & odr =
121  getParam<std::vector<NonlinearVariableName>>("off_diag_row");
122  const std::vector<NonlinearVariableName> & odc =
123  getParam<std::vector<NonlinearVariableName>>("off_diag_column");
124 
125  for (const auto i : index_range(odr))
126  {
127  unsigned int row = _nl.system().variable_number(odr[i]);
128  unsigned int column = _nl.system().variable_number(odc[i]);
129  off_diag[row].push_back(column);
130  }
131  }
132  // Add all of the preconditioning systems
133  for (unsigned int var = 0; var < n_vars; var++)
134  addSystem(var, off_diag[var], _pre_type[var]);
135 
137 
139  mooseError("PBP must be used with JFNK solve type");
140 }
141 
143 
144 void
146  std::vector<unsigned int> off_diag,
147  PreconditionerType type)
148 {
149  std::string var_name = _nl.system().variable_name(var);
150 
151  LinearImplicitSystem & precond_system =
152  _fe_problem.es().add_system<LinearImplicitSystem>(var_name + "_system");
153  precond_system.assemble_before_solve = false;
154 
155  const std::set<SubdomainID> * active_subdomains = _nl.getVariableBlocks(var);
156  precond_system.add_variable(
157  var_name + "_prec", _nl.system().variable(var).type(), active_subdomains);
158 
159  _systems[var] = &precond_system;
160  _pre_type[var] = type;
161 
162  _off_diag_mats[var].resize(off_diag.size());
163  for (const auto i : index_range(off_diag))
164  {
165  // Add the matrix to hold the off-diagonal piece
166  _off_diag_mats[var][i] = &precond_system.add_matrix(_nl.system().variable_name(off_diag[i]));
167  }
168 
169  _off_diag[var] = off_diag;
170 }
171 
172 void
174 {
175  TIME_SECTION("init", 2, "Initializing PhysicsBasedPreconditioner");
176 
177  // Tell libMesh that this is initialized!
178  _is_initialized = true;
179 
180  const unsigned int num_systems = _systems.size();
181 
182  // If no order was specified, just solve them in increasing order
183  if (_solve_order.size() == 0)
184  {
185  _solve_order.resize(num_systems);
186  for (unsigned int i = 0; i < num_systems; i++)
187  _solve_order[i] = i;
188  }
189 
190  // Loop over variables
191  for (unsigned int system_var = 0; system_var < num_systems; system_var++)
192  {
193  LinearImplicitSystem & u_system = *_systems[system_var];
194 
195  if (!_preconditioners[system_var])
196  _preconditioners[system_var] =
198 
199  // we have to explicitly set the matrix in the preconditioner, because h-adaptivity could have
200  // changed it and we have to work with the current one
201  Preconditioner<Number> * preconditioner = _preconditioners[system_var].get();
202  preconditioner->set_matrix(u_system.get_system_matrix());
203  preconditioner->set_type(_pre_type[system_var]);
204 
205  preconditioner->init();
206  }
207 }
208 
209 void
211 {
212  const unsigned int num_systems = _systems.size();
213 
214  std::vector<JacobianBlock *> blocks;
215 
216  // Loop over variables
217  for (unsigned int system_var = 0; system_var < num_systems; system_var++)
218  {
219  LinearImplicitSystem & u_system = *_systems[system_var];
220 
221  {
222  JacobianBlock * block =
223  new JacobianBlock(u_system, u_system.get_system_matrix(), system_var, system_var);
224  blocks.push_back(block);
225  }
226 
227  for (const auto diag : index_range(_off_diag[system_var]))
228  {
229  unsigned int coupled_var = _off_diag[system_var][diag];
230  std::string coupled_name = _nl.system().variable_name(coupled_var);
231 
232  JacobianBlock * block =
233  new JacobianBlock(u_system, *_off_diag_mats[system_var][diag], system_var, coupled_var);
234  blocks.push_back(block);
235  }
236  }
237 
239 
240  // cleanup
241  for (auto & block : blocks)
242  delete block;
243 }
244 
245 void
247 {
248  TIME_SECTION("apply", 1, "Applying PhysicsBasedPreconditioner");
249 
250  const unsigned int num_systems = _systems.size();
251 
253 
254  // Zero out the solution vectors
255  for (unsigned int sys = 0; sys < num_systems; sys++)
256  _systems[sys]->solution->zero();
257 
258  // Loop over solve order
259  for (const auto i : index_range(_solve_order))
260  {
261  unsigned int system_var = _solve_order[i];
262 
263  LinearImplicitSystem & u_system = *_systems[system_var];
264 
265  // Copy rhs from the big system into the small one
267  mesh, _nl.system().number(), system_var, x, u_system.number(), 0, *u_system.rhs);
268 
269  // Modify the RHS by subtracting off the matvecs of the solutions for the other preconditioning
270  // systems with the off diagonal blocks in this system.
271  for (const auto diag : index_range(_off_diag[system_var]))
272  {
273  unsigned int coupled_var = _off_diag[system_var][diag];
274  LinearImplicitSystem & coupled_system = *_systems[coupled_var];
275  SparseMatrix<Number> & off_diag = *_off_diag_mats[system_var][diag];
276  NumericVector<Number> & rhs = *u_system.rhs;
277 
278  // This next bit computes rhs -= A*coupled_solution
279  // It does what it does because there is no vector_mult_sub()
280  rhs.close();
281  rhs.scale(-1.0);
282  rhs.close();
283  off_diag.vector_mult_add(rhs, *coupled_system.solution);
284  rhs.close();
285  rhs.scale(-1.0);
286  rhs.close();
287  }
288 
289  // If there is no off_diag, then u_system.rhs will not be closed.
290  // Thus, we need to close it right here
291  if (!_off_diag[system_var].size())
292  u_system.rhs->close();
293 
294  // Apply the preconditioner to the small system
295  _preconditioners[system_var]->apply(*u_system.rhs, *u_system.solution);
296 
297  // Copy solution from small system into the big one
298  // copyVarValues(mesh,system,0,*u_system.solution,0,system_var,y);
299  }
300 
301  // Copy the solutions out
302  for (unsigned int system_var = 0; system_var < num_systems; system_var++)
303  {
304  LinearImplicitSystem & u_system = *_systems[system_var];
305 
307  mesh, u_system.number(), 0, *u_system.solution, _nl.system().number(), system_var, y);
308  }
309 
310  y.close();
311 }
312 
313 void
315 {
316 }
virtual void setup()
This is called every time the "operator might have changed".
virtual const std::set< SubdomainID > * getVariableBlocks(unsigned int var_number)
Get the block where a variable of this system is defined.
Definition: SystemBase.C:163
Helper class for holding the preconditioning blocks to fill.
virtual void apply(const NumericVector< Number > &x, NumericVector< Number > &y)
Computes the preconditioned vector "y" based on input "x".
static void copyVarValues(MeshBase &mesh, const unsigned int from_system, const unsigned int from_var, const NumericVector< Number > &from_vector, const unsigned int to_system, const unsigned int to_var, NumericVector< Number > &to_vector)
Helper function for copying values associated with variables in vectors from two different systems...
std::vector< std::vector< unsigned int > > _off_diag
Holds which off diagonal blocks to compute.
const Variable & variable(unsigned int var) const
std::vector< libMesh::LinearImplicitSystem * > _systems
List of linear system that build up the preconditioner.
char ** blocks
std::vector< libMesh::PreconditionerType > _pre_type
Which preconditioner to use for each solve.
PreconditionerType type() const
MeshBase & mesh
NonlinearSystemBase & _nl
The nonlinear system this PBP is associated with (convenience reference)
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
NumericVector< Number > * rhs
void set_matrix(SparseMatrix< Number > &mat)
FEProblemBase & _fe_problem
Subproblem this preconditioner is part of.
const Parallel::Communicator & _communicator
virtual void attachPreconditioner(libMesh::Preconditioner< Number > *preconditioner)=0
Attach a customized preconditioner that requires physics knowledge.
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
virtual void clear()
Release all memory and clear data structures.
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
Base class for MOOSE preconditioners.
unsigned int variable_number(std::string_view var) const
virtual void init()
Initialize data structures if not done so already.
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
virtual unsigned int nVariables() const
Get the number of variables in this system.
Definition: SystemBase.C:874
virtual void computeJacobianBlocks(std::vector< JacobianBlock *> &blocks, const unsigned int nl_sys_num)
Computes several Jacobian blocks simultaneously, summing their contributions into smaller preconditio...
virtual void scale(const T factor)=0
unsigned int number() const
static InputParameters validParams()
Constructor.
const std::string _type
The type of this class.
Definition: MooseBase.h:87
unsigned int n_vars
PhysicsBasedPreconditioner(const InputParameters &params)
Jacobian-Free Newton Krylov.
Definition: MooseTypes.h:845
virtual libMesh::EquationSystems & es() override
std::unique_ptr< NumericVector< Number > > solution
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition: MooseMesh.h:88
Implements a segregated solve preconditioner.
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
const std::string & variable_name(const unsigned int i) const
static InputParameters validParams()
unsigned int number() const
Gets the number of this system.
Definition: SystemBase.C:1159
std::vector< std::vector< libMesh::SparseMatrix< Number > * > > _off_diag_mats
Holds pointers to the off-diagonal matrices.
virtual void close()=0
PreconditionerType
void vector_mult_add(NumericVector< T > &dest, const NumericVector< T > &arg) const
virtual MooseMesh & mesh() override
std::vector< unsigned int > _solve_order
Holds the order the blocks are solved for.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
SolverParams & solverParams(unsigned int solver_sys_num=0)
Get the solver parameters.
void setCouplingMatrix(std::unique_ptr< libMesh::CouplingMatrix > cm)
Setup the coupling matrix on the finite element problem.
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
void set_type(const PreconditionerType pct)
std::vector< std::unique_ptr< libMesh::Preconditioner< Number > > > _preconditioners
Holds one Preconditioner object per small system to solve.
registerMooseObjectAliased("MooseApp", PhysicsBasedPreconditioner, "PBP")
Real Number
unsigned int n_vars() const
virtual System & add_system(std::string_view system_type, std::string_view name)
SparseMatrix< Number > & add_matrix(std::string_view mat_name, ParallelType type=PARALLEL, MatrixBuildType mat_build_type=MatrixBuildType::AUTOMATIC)
void addSystem(unsigned int var, std::vector< unsigned int > off_diag, libMesh::PreconditionerType type=libMesh::AMG_PRECOND)
Add a diagonal system + possibly off-diagonals ones as well, also specifying type of preconditioning...
const SparseMatrix< Number > & get_system_matrix() const
auto index_range(const T &sizable)
const FEType & type() const
virtual libMesh::System & system() override
Get the reference to the libMesh system.