https://mooseframework.inl.gov
Loading...
Searching...
No Matches
MFEMGeometricMultigridSolver.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
10#ifdef MOOSE_MFEM_ENABLED
11
13#include "MFEMProblem.h"
14#include "EquationSystem.h"
15
17
19{
20 // MGProxy is installed as a preconditioner, so Mult() should overwrite its output vector rather
21 // than treating it as an initial iterate. This matches MFEM's
22 // IterativeSolver::SetPreconditioner() convention, which sets the preconditioner's iterative_mode
23 // to false.
24 iterative_mode = false;
25}
26
27void
29{
30 _mg = &mg;
31 height = mg.Height();
32 width = mg.Width();
33}
34
35void
37{
38 _owner.BuildMultigrid(op);
39}
40
41void
42MFEMGeometricMultigridSolver::MGProxy::Mult(const mfem::Vector & x, mfem::Vector & y) const
43{
44 MFEM_VERIFY(_mg, "MGProxy: GeometricMultigrid not yet built");
45 _mg->Mult(x, y);
46}
47
50{
53 "Geometric (p-)multigrid preconditioner backed by mfem::GeometricMultigrid. "
54 "Requires a linear equation system, an MFEMFESpaceHierarchy, and per-level smoother "
55 "objects.");
56
57 params.addRequiredParam<std::string>("variable",
58 "Name of the trial variable this preconditioner acts on.");
59 params.addRequiredParam<std::vector<MFEMSolverName>>(
60 "smoothers",
61 "Names of LinearSolverBase objects used as smoothers on the interior levels "
62 "(levels 1 to N-1). May have length 1 (used on all interior levels) or "
63 "N-1 (one per interior level, ordered coarse-to-fine).");
64 params.addRequiredParam<MFEMSolverName>(
65 "coarse_solver", "Name of the LinearSolverBase used on the coarsest level.");
66 params.addParam<std::vector<std::string>>(
67 "assembly_levels",
68 {"legacy"},
69 "Assembly level for each level in the hierarchy. Valid values: 'legacy', 'full', "
70 "'element', 'partial', 'none'. May have length 1 (used on all N levels) or N.");
71 return params;
72}
73
76 _var_name(getParam<std::string>("variable")),
77 _smoother_names(getParam<std::vector<MFEMSolverName>>("smoothers")),
78 _coarse_solver_name(getParam<MFEMSolverName>("coarse_solver"))
79{
80 auto & problem = getMFEMProblem();
81 auto eq_sys = problem.getProblemData().eqn_system;
82
83 if (eq_sys->IsEigen() || eq_sys->IsComplex())
84 mooseError("GeometricMultigridSolver '", name(), "': requires a real, non-eigen eq. system");
85
86 // Co-own the hierarchy so it outlives this solver.
87 if (auto * hierarchy_name = problem.getMFEMObject<MFEMVariable>("MooseVariableBase", _var_name)
88 .queryParam<std::string>("fespace_hierarchy"))
89 _hierarchy = problem.getProblemData().fespace_hierarchies.GetShared(*hierarchy_name);
90 else
91 paramError("variable", "must be associated with an MFEMFESpaceHierarchy.");
92
93 // Parse assembly levels, optionally expanding a single input value to all levels.
94 const int N = _hierarchy->GetNumLevels();
95 mooseAssert(N, "Malformed MFEMFESpaceHierarchy w/ no levels");
96 const auto & asm_strs = getParam<std::vector<std::string>>("assembly_levels");
97 const int n_asm = asm_strs.size();
98 if (n_asm != 1 && n_asm != N)
100 "assembly_levels", "must have length 1 or N = ", N, " (total levels), got ", n_asm, ".");
101
102 _assembly_levels.resize(N);
103 for (const auto i : make_range(N))
104 _assembly_levels[i] = ParseAssemblyLevel(n_asm == 1 ? asm_strs[0] : asm_strs[i]);
105
107}
108
109void
111{
112 _mg.reset();
113 _level_ops.clear();
114 _level_blfs.clear();
115
116 auto proxy = std::make_unique<MGProxy>(*this);
117 _mg_proxy = proxy.get();
118 _solver = std::move(proxy);
119}
120
121mfem::AssemblyLevel
123{
124 static MooseEnum assembly_levels("legacy full element partial none", "legacy");
125 return (assembly_levels = s).getEnum<mfem::AssemblyLevel>();
126}
127
128void
130{
131 BuildMultigrid(op);
132}
133
134void
136{
137 auto & problem = getMFEMProblem();
138 auto eq_sys = problem.getProblemData().eqn_system;
139
140 if (eq_sys->IsNonlinear() || eq_sys->IsMultivariate())
141 mooseError("GeometricMultigridSolver '", name(), "': requires a univariate, linear eq. system");
142
143 const int N = _hierarchy->GetNumLevels();
144 const int finest_level = _hierarchy->GetFinestLevelIndex();
145
146 // Validate smoother vector length (levels 1 to N-1 each need a smoother).
147 const int n_smooth = _smoother_names.size();
148 if (n_smooth != 1 && n_smooth != N - 1)
149 paramError("smoothers", "must have length 1 or N-1 = ", N - 1, ", got ", n_smooth, ".");
150
151 auto get_smoother = [&](int level) -> Moose::MFEM::LinearSolverBase &
152 {
153 if (level == 0)
154 return problem.getMFEMObject<Moose::MFEM::LinearSolverBase>("Moose::MFEM::SolverBase",
156 const std::string & sname = (n_smooth == 1) ? _smoother_names[0] : _smoother_names[level - 1];
157 return problem.getMFEMObject<Moose::MFEM::LinearSolverBase>("Moose::MFEM::SolverBase", sname);
158 };
159
160 // Obtain essential boundary attribute markers from the equation system.
161 mfem::Array<int> & ess_bdr = eq_sys->GetEssentialBoundaryMarkers(_var_name);
162
163 auto & finest_fespace =
164 cast_ref<mfem::ParFiniteElementSpace &>(_hierarchy->GetFESpaceAtLevel(finest_level));
165 const int finest_size = finest_fespace.GetTrueVSize();
166 if (op.Height() != finest_size || op.Width() != finest_size)
167 mooseError("GeometricMultigridSolver '",
168 name(),
169 "': incoming fine operator has size ",
170 op.Height(),
171 " x ",
172 op.Width(),
173 ", but the finest hierarchy space has true size ",
174 finest_size,
175 ".");
176
177 // Build new levels' forms; accumulate before touching _mg / _level_*.
178 std::vector<std::shared_ptr<mfem::ParBilinearForm>> new_blfs;
179 std::vector<std::unique_ptr<mfem::OperatorHandle>> new_level_ops;
180 new_level_ops.reserve(N - 1);
181
182 auto mg = std::make_unique<mfem::GeometricMultigrid>(*_hierarchy, ess_bdr);
183 auto * mg_ptr = mg.get();
184
185 for (const auto level : make_range(N))
186 {
187 auto & level_fespace =
188 cast_ref<mfem::ParFiniteElementSpace &>(_hierarchy->GetFESpaceAtLevel(level));
189
190 // Compute essential true DoFs for this level.
191 mfem::Array<int> level_tdofs;
192 level_fespace.GetEssentialTrueDofs(ess_bdr, level_tdofs);
193
194 // Build level operator.
195 mfem::Operator * level_op = nullptr;
196
197 if (level == finest_level)
198 level_op = const_cast<mfem::Operator *>(&op);
199 else
200 {
201 auto blf =
202 eq_sys->BuildBilinearFormForFESpace(_var_name, level_fespace, _assembly_levels[level]);
203
204 auto level_op_handle = std::make_unique<mfem::OperatorHandle>();
205 blf->FormSystemMatrix(level_tdofs, *level_op_handle);
206 level_op = level_op_handle->Ptr();
207 new_level_ops.push_back(std::move(level_op_handle));
208 new_blfs.push_back(std::move(blf));
209 }
210
211 // Configure the smoother / coarse solver with this level's operator.
212 // Each smoother's SetOperator() owns full initialization.
213 auto & level_smoother = get_smoother(level);
214 level_smoother.SetOperator(*level_op);
215
216 mg_ptr->AddLevel(
217 level_op, &level_smoother.GetSolver(), /*ownOperator=*/false, /*ownSmoother=*/false);
218 }
219
220 // Atomically replace:
221 // 1. Old MG freed, dropping raw pointers into level operators.
222 // 2. Old operator handles freed before old forms they may wrap.
223 // 3. Proxy updated to point at the new MG and level data.
224 _mg = std::move(mg);
225 _level_ops = std::move(new_level_ops);
226 _level_blfs = std::move(new_blfs);
228}
229#endif
registerMooseObject("MooseApp", MFEMGeometricMultigridSolver)
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object.
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.
void SetOperator(const mfem::Operator &op) override
Rebuilds the owner's multigrid hierarchy for the new outer-solver operator.
void SetMG(mfem::GeometricMultigrid &mg)
Updates the concrete MFEM multigrid object used by Mult().
void Mult(const mfem::Vector &x, mfem::Vector &y) const override
Applies the current concrete MFEM multigrid preconditioner.
MGProxy(MFEMGeometricMultigridSolver &owner)
Constructs a proxy that delegates multigrid rebuilding to the owning MOOSE solver.
P-multigrid / geometric multigrid preconditioner backed by mfem::GeometricMultigrid.
std::unique_ptr< mfem::GeometricMultigrid > _mg
Concrete MFEM multigrid preconditioner rebuilt on each SetOperator() call.
const std::string _var_name
Trial variable whose operator is preconditioned by this solver.
std::shared_ptr< mfem::ParFiniteElementSpaceHierarchy > _hierarchy
Finite element space hierarchy defining the multigrid levels.
MFEMGeometricMultigridSolver(const InputParameters &parameters)
mfem::AssemblyLevel ParseAssemblyLevel(const std::string &s) const
Map assembly-level string ("legacy", "full", "element", "partial", "none") to the corresponding mfem:...
void ConstructSolver() override
Creates a stable proxy solver; the real multigrid is built when the proxy gets an operator.
std::vector< mfem::AssemblyLevel > _assembly_levels
Assembly level requested for each multigrid level after optional single-value expansion.
std::vector< std::shared_ptr< mfem::ParBilinearForm > > _level_blfs
Rediscretized bilinear forms kept alive for the active linear coarse-level operators.
MGProxy * _mg_proxy
Non-owning pointer to the proxy solver stored in _solver.
void BuildMultigrid(const mfem::Operator &op)
Rebuild the multigrid object and per-level operators for the supplied finest-level operator.
const std::vector< MFEMSolverName > _smoother_names
Names of solvers used as smoothers on interior multigrid levels.
void SetOperatorImpl(mfem::Operator &op) override
Rebuilds the multigrid hierarchy for the supplied finest-level operator.
const MFEMSolverName _coarse_solver_name
Name of the solver used on the coarsest multigrid level.
std::vector< std::unique_ptr< mfem::OperatorHandle > > _level_ops
Constrained linear coarse-level operators; destroyed before the forms that own their data.
MFEMProblem & getMFEMProblem()
Return the owning MFEM problem.
Definition MFEMObject.h:45
Constructs and stores an mfem::ParGridFunction object.
const InputParameters & parameters() const
Get the parameters of the object.
Definition MooseBase.h:131
const std::string & name() const
Get the name of the class.
Definition MooseBase.h:103
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition MooseBase.h:457
const T * queryParam(const std::string &name) const
Query a parameter for the object.
Definition MooseBase.h:413
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
Definition MooseBase.h:406
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
Base class for linear MFEM solvers and preconditioners.
static InputParameters validParams()
LinearSolverBase(const InputParameters &parameters)
std::unique_ptr< mfem::Solver > _solver
Solver to be used for the problem.
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...