https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
31using 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),
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
144void
146 std::vector<unsigned int> off_diag,
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
172void
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
209void
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
245void
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
313void
char ** blocks
registerMooseObjectAliased("MooseApp", PhysicsBasedPreconditioner, "PBP")
unsigned int n_vars
virtual libMesh::EquationSystems & es() override
SolverParams & solverParams(unsigned int solver_sys_num=0)
Get the solver parameters.
virtual MooseMesh & mesh() override
virtual void computeJacobianBlocks(std::vector< JacobianBlock * > &blocks, const unsigned int nl_sys_num)
Computes several Jacobian blocks simultaneously, summing their contributions into smaller preconditio...
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
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...
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.
Helper class for holding the preconditioning blocks to fill.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition MooseBase.h:271
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition MooseBase.h:199
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition MooseMesh.h:95
Base class for MOOSE preconditioners.
FEProblemBase & _fe_problem
Subproblem this preconditioner is part of.
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.
void setCouplingMatrix(std::unique_ptr< libMesh::CouplingMatrix > cm)
Setup the coupling matrix on the finite element problem.
static InputParameters validParams()
virtual void attachPreconditioner(libMesh::Preconditioner< Number > *preconditioner)=0
Attach a customized preconditioner that requires physics knowledge.
virtual libMesh::System & system() override
Get the reference to the libMesh system.
Implements a segregated solve preconditioner.
virtual void setup()
This is called every time the "operator might have changed".
std::vector< unsigned int > _solve_order
Holds the order the blocks are solved for.
virtual void clear()
Release all memory and clear data structures.
static InputParameters validParams()
Constructor.
std::vector< std::vector< libMesh::SparseMatrix< Number > * > > _off_diag_mats
Holds pointers to the off-diagonal matrices.
NonlinearSystemBase & _nl
The nonlinear system this PBP is associated with (convenience reference)
virtual void apply(const NumericVector< Number > &x, NumericVector< Number > &y)
Computes the preconditioned vector "y" based on input "x".
std::vector< libMesh::LinearImplicitSystem * > _systems
List of linear system that build up the preconditioner.
std::vector< std::unique_ptr< libMesh::Preconditioner< Number > > > _preconditioners
Holds one Preconditioner object per small system to solve.
PhysicsBasedPreconditioner(const InputParameters &params)
std::vector< libMesh::PreconditionerType > _pre_type
Which preconditioner to use for each solve.
std::vector< std::vector< unsigned int > > _off_diag
Holds which off diagonal blocks to compute.
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.
virtual void init()
Initialize data structures if not done so already.
Moose::SolveType _type
virtual unsigned int nVariables() const
Get the number of variables in this system.
Definition SystemBase.C:890
unsigned int number() const
Gets the number of this system.
virtual const std::set< SubdomainID > * getVariableBlocks(unsigned int var_number)
Get the block where a variable of this system is defined.
Definition SystemBase.C:165
virtual System & add_system(std::string_view system_type, std::string_view name)
NumericVector< Number > * rhs
const SparseMatrix< Number > & get_system_matrix() const
virtual void close()=0
virtual void scale(const T factor)=0
const Parallel::Communicator & _communicator
void set_type(const PreconditionerType pct)
void set_matrix(SparseMatrix< Number > &mat)
PreconditionerType type() const
static std::unique_ptr< Preconditioner< T > > build_preconditioner(const libMesh::Parallel::Communicator &comm, const SolverPackage solver_package=libMesh::default_solver_package())
void vector_mult_add(NumericVector< T > &dest, const NumericVector< T > &arg) const
SparseMatrix< Number > & add_matrix(std::string_view mat_name, ParallelType type=PARALLEL, MatrixBuildType mat_build_type=MatrixBuildType::AUTOMATIC)
const Variable & variable(unsigned int var) const
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
std::unique_ptr< NumericVector< Number > > solution
const std::string & variable_name(const unsigned int i) const
unsigned int variable_number(std::string_view var) const
unsigned int n_vars() const
unsigned int number() const
const FEType & type() const
MeshBase & mesh
@ ST_JFNK
Jacobian-Free Newton Krylov.
Definition MooseTypes.h:899
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
auto index_range(const T &sizable)
Real Number
PreconditionerType