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
82 if (_equation_system->IsEigen() || _equation_system->IsComplex())
83 mooseError("GeometricMultigridSolver '", name(), "': requires a real, non-eigen eq. system");
84
85 // Co-own the hierarchy so it outlives this solver.
86 if (auto * hierarchy_name = problem.getMFEMObject<MFEMVariable>("MooseVariableBase", _var_name)
87 .queryParam<std::string>("fespace_hierarchy"))
88 _hierarchy = problem.getProblemData().fespace_hierarchies.GetShared(*hierarchy_name);
89 else
90 paramError("variable", "must be associated with an MFEMFESpaceHierarchy.");
91
92 // Parse assembly levels, optionally expanding a single input value to all levels.
93 const int N = _hierarchy->GetNumLevels();
94 mooseAssert(N, "Malformed MFEMFESpaceHierarchy w/ no levels");
95 const auto & asm_strs = getParam<std::vector<std::string>>("assembly_levels");
96 const int n_asm = asm_strs.size();
97 if (n_asm != 1 && n_asm != N)
99 "assembly_levels", "must have length 1 or N = ", N, " (total levels), got ", n_asm, ".");
100
101 _assembly_levels.resize(N);
102 for (const auto i : make_range(N))
103 _assembly_levels[i] = ParseAssemblyLevel(n_asm == 1 ? asm_strs[0] : asm_strs[i]);
104
106}
107
108void
110{
111 _mg.reset();
112 _level_ops.clear();
113 _level_blfs.clear();
114
115 auto proxy = std::make_unique<MGProxy>(*this);
116 _mg_proxy = proxy.get();
117 _solver = std::move(proxy);
118}
119
120mfem::AssemblyLevel
122{
123 static MooseEnum assembly_levels("legacy full element partial none", "legacy");
124 return (assembly_levels = s).getEnum<mfem::AssemblyLevel>();
125}
126
127void
129{
130 BuildMultigrid(op);
131}
132
133void
135{
136 auto & problem = getMFEMProblem();
137
138 if (_equation_system->IsNonlinear() || _equation_system->IsMultivariate())
139 mooseError("GeometricMultigridSolver '", name(), "': requires a univariate, linear eq. system");
140
141 const int N = _hierarchy->GetNumLevels();
142 const int finest_level = _hierarchy->GetFinestLevelIndex();
143
144 // Validate smoother vector length (levels 1 to N-1 each need a smoother).
145 const int n_smooth = _smoother_names.size();
146 if (n_smooth != 1 && n_smooth != N - 1)
147 paramError("smoothers", "must have length 1 or N-1 = ", N - 1, ", got ", n_smooth, ".");
148
149 auto get_smoother = [&](int level) -> Moose::MFEM::LinearSolverBase &
150 {
151 if (level == 0)
152 return problem.getMFEMObject<Moose::MFEM::LinearSolverBase>("Moose::MFEM::SolverBase",
154 const std::string & sname = (n_smooth == 1) ? _smoother_names[0] : _smoother_names[level - 1];
155 return problem.getMFEMObject<Moose::MFEM::LinearSolverBase>("Moose::MFEM::SolverBase", sname);
156 };
157
158 // Obtain essential boundary attribute markers from the equation system.
159 mfem::Array<int> & ess_bdr = _equation_system->GetEssentialBoundaryMarkers(_var_name);
160
161 auto & finest_fespace =
162 cast_ref<mfem::ParFiniteElementSpace &>(_hierarchy->GetFESpaceAtLevel(finest_level));
163 const int finest_size = finest_fespace.GetTrueVSize();
164 if (op.Height() != finest_size || op.Width() != finest_size)
165 mooseError("GeometricMultigridSolver '",
166 name(),
167 "': incoming fine operator has size ",
168 op.Height(),
169 " x ",
170 op.Width(),
171 ", but the finest hierarchy space has true size ",
172 finest_size,
173 ".");
174
175 // Build new levels' forms; accumulate before touching _mg / _level_*.
176 std::vector<std::shared_ptr<mfem::ParBilinearForm>> new_blfs;
177 std::vector<std::unique_ptr<mfem::OperatorHandle>> new_level_ops;
178 new_level_ops.reserve(N - 1);
179
180 auto mg = std::make_unique<mfem::GeometricMultigrid>(*_hierarchy, ess_bdr);
181 auto * mg_ptr = mg.get();
182
183 for (const auto level : make_range(N))
184 {
185 auto & level_fespace =
186 cast_ref<mfem::ParFiniteElementSpace &>(_hierarchy->GetFESpaceAtLevel(level));
187
188 // Compute essential true DoFs for this level.
189 mfem::Array<int> level_tdofs;
190 level_fespace.GetEssentialTrueDofs(ess_bdr, level_tdofs);
191
192 // Build level operator.
193 mfem::Operator * level_op = nullptr;
194
195 if (level == finest_level)
196 level_op = const_cast<mfem::Operator *>(&op);
197 else
198 {
199 auto blf = _equation_system->BuildBilinearFormForFESpace(
200 _var_name, level_fespace, _assembly_levels[level]);
201
202 auto level_op_handle = std::make_unique<mfem::OperatorHandle>();
203 blf->FormSystemMatrix(level_tdofs, *level_op_handle);
204 level_op = level_op_handle->Ptr();
205 new_level_ops.push_back(std::move(level_op_handle));
206 new_blfs.push_back(std::move(blf));
207 }
208
209 // Configure the smoother / coarse solver with this level's operator.
210 // Each smoother's SetOperator() owns full initialization.
211 auto & level_smoother = get_smoother(level);
212 level_smoother.SetOperator(*level_op);
213
214 mg_ptr->AddLevel(
215 level_op, &level_smoother.GetSolver(), /*ownOperator=*/false, /*ownSmoother=*/false);
216 }
217
218 // Atomically replace:
219 // 1. Old MG freed, dropping raw pointers into level operators.
220 // 2. Old operator handles freed before old forms they may wrap.
221 // 3. Proxy updated to point at the new MG and level data.
222 _mg = std::move(mg);
223 _level_ops = std::move(new_level_ops);
224 _level_blfs = std::move(new_blfs);
226}
227#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.
void SetOperatorImpl(const mfem::Operator &op) override
Rebuilds the multigrid hierarchy for the supplied finest-level operator.
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.
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.
std::shared_ptr< EquationSystem > _equation_system
Pointer to EquationSystem used for problem-specific solver setup.
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...