https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ComplexEquationSystem.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 "CoefficientManager.h"
14#include "libmesh/int_range.h"
15
16namespace Moose::MFEM
17{
18
19void
21 ComplexGridFunctions & cmplx_gridfunctions,
22 mfem::AssemblyLevel assembly_level)
23{
24 _assembly_level = assembly_level;
25
26 if (gridfunctions.size())
27 mooseError("Mixing real and complex variables is currently not supported.");
28
29 for (auto & test_var_name : _test_var_names)
30 {
31 if (!cmplx_gridfunctions.Has(test_var_name))
32 {
33 mooseError("MFEM complex variable ",
34 test_var_name,
35 " requested by equation system during initialization was "
36 "not found in gridfunctions");
37 }
38 // Store pointers to test FESpaces
39 _test_pfespaces.push_back(cmplx_gridfunctions.Get(test_var_name)->ParFESpace());
40 // Create auxiliary gridfunctions for storing essential constraints from Dirichlet conditions
41 _cmplx_var_ess_constraints.emplace_back(std::make_unique<mfem::ParComplexGridFunction>(
42 cmplx_gridfunctions.Get(test_var_name)->ParFESpace()));
43 }
44
45 // Store pointers to FESpaces of all coupled variables
46 for (auto & coupled_var_name : _coupled_var_names)
47 _coupled_pfespaces.push_back(cmplx_gridfunctions.Get(coupled_var_name)->ParFESpace());
48
49 // Extract which coupled variables are to be trivially eliminated and which are trial variables
51
52 // Store pointers to coupled variable ComplexGridFunctions that are to be eliminated prior to
53 // forming the jacobian
54 for (auto & eliminated_var_name : _eliminated_var_names)
55 _cmplx_eliminated_variables.Register(eliminated_var_name,
56 cmplx_gridfunctions.GetShared(eliminated_var_name));
57
58 // Get a reference to the complex GridFunctions
59 _complex_gfuncs = &cmplx_gridfunctions;
60}
61
62void
64{
65 // Register linear forms
66 for (const auto i : index_range(_test_var_names))
67 {
68 auto test_var_name = _test_var_names.at(i);
69 _clfs.Register(test_var_name,
70 std::make_shared<mfem::ParComplexLinearForm>(_test_pfespaces.at(i)));
71 _clfs.GetRef(test_var_name) = 0.0;
72 }
73
74 for (auto & test_var_name : _test_var_names)
75 {
76 // Apply kernels
77 auto clf = _clfs.GetShared(test_var_name);
80 clf->Assemble();
81 }
82
83 // Apply boundary conditions
85
86 // Eliminate trivially eliminated variables by subtracting contributions from linear forms
88}
89
90void
92{
93 // Register bilinear forms
94 for (const auto i : index_range(_test_var_names))
95 {
96 auto test_var_name = _test_var_names.at(i);
97 _slfs.Register(test_var_name,
98 std::make_shared<mfem::ParSesquilinearForm>(_test_pfespaces.at(i)));
99
100 // Apply kernels
101 auto slf = _slfs.GetShared(test_var_name);
102 slf->SetAssemblyLevel(_assembly_level);
103 ApplyBoundaryBLFIntegrators<mfem::ParSesquilinearForm>(
104 test_var_name, test_var_name, slf, _cmplx_integrated_bc_map);
105 ApplyDomainBLFIntegrators<mfem::ParSesquilinearForm>(
106 test_var_name, test_var_name, slf, _cmplx_kernels_map);
107 // Assemble
108 slf->Assemble();
109 slf->Finalize();
110 }
111}
112
113void
115{
116 // Register mixed sesquilinear forms. Note that not all combinations may
117 // have a kernel.
118
119 // Create mslf for each test/coupled variable pair with an added kernel.
120 // Mixed sesquilinear forms with coupled variables that are not trial variables are
121 // associated with contributions from eliminated variables.
122 for (const auto i : index_range(_test_var_names))
123 {
124 auto test_var_name = _test_var_names.at(i);
125 auto test_mslfs =
126 std::make_shared<Moose::MFEM::NamedFieldsMap<mfem::ParMixedSesquilinearForm>>();
127 for (const auto j : index_range(_coupled_var_names))
128 {
129 const auto & coupled_var_name = _coupled_var_names.at(j);
130 auto mslf = std::make_shared<mfem::ParMixedSesquilinearForm>(_coupled_pfespaces.at(j),
131 _test_pfespaces.at(i));
132 // Register MixedSesquilinearForm if kernels exist for it, and assemble
133 // kernels
134 if (_cmplx_kernels_map.Has(test_var_name) &&
135 _cmplx_kernels_map.Get(test_var_name)->Has(coupled_var_name) &&
136 test_var_name != coupled_var_name)
137 {
138 mslf->SetAssemblyLevel(_assembly_level);
139 // Apply all mixed kernels with this test/trial pair
140 ApplyDomainBLFIntegrators<mfem::ParMixedSesquilinearForm>(
141 coupled_var_name, test_var_name, mslf, _cmplx_kernels_map);
142 // Assemble mixed bilinear forms
143 mslf->Assemble();
144 mslf->Finalize();
145 // Register mixed bilinear forms associated with a single trial variable
146 // for the current test variable
147 test_mslfs->Register(coupled_var_name, mslf);
148 }
149 }
150 // Register all mixed bilinear form sets associated with a single test
151 // variable
152 _mslfs.Register(test_var_name, test_mslfs);
153 }
154}
155
156void
158 mfem::ParComplexGridFunction & trial_gf,
159 mfem::Array<int> & global_ess_markers)
160{
161 if (_cmplx_essential_bc_map.Has(var_name))
162 for (auto & bc : _cmplx_essential_bc_map.GetRef(var_name))
163 {
164 // Set constrained DoFs values on essential boundaries
165 bc->ApplyBC(trial_gf);
166 // Fetch marker array labelling essential boundaries of current BC
167 mfem::Array<int> ess_bdrs(bc->getBoundaryMarkers());
168 // Add these boundary markers to the set of markers labelling all essential boundaries
169 for (const auto i : make_range(ess_bdrs.Size()))
170 global_ess_markers[i] |= ess_bdrs[i];
171 }
172}
173
174void
176{
177 _ess_tdof_lists.resize(_trial_var_names.size());
178 _ess_markers.resize(_trial_var_names.size());
179 for (const auto i : index_range(_trial_var_names))
180 {
181 const auto & trial_var_name = _trial_var_names.at(i);
182 mfem::ParComplexGridFunction & trial_gf = *_cmplx_var_ess_constraints.at(i);
183
184 // Make sure we update the size, if this mesh has changed recently for instance
185 trial_gf.Update();
186
187 // Initial guess for iterative solvers (initial condition or the previous time step solution)
188 cast_ref<mfem::Vector &>(trial_gf) = _complex_gfuncs->GetRef(trial_var_name);
189
190 _ess_markers.at(i).SetSize(trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max(), 0);
191 // Set strongly constrained DoFs of trial_gf on essential boundaries and add markers for all
192 // essential boundaries to the _ess_markers array
193 ApplyComplexEssentialBC(trial_var_name, trial_gf, _ess_markers.at(i));
194 trial_gf.ParFESpace()->GetEssentialTrueDofs(_ess_markers.at(i), _ess_tdof_lists.at(i));
195 }
196}
197
198void
199ComplexEquationSystem::AddComplexKernel(std::shared_ptr<MFEMComplexKernel> kernel)
200{
201 const auto & trial_var_name = kernel->getTrialVariableName();
202 const auto & test_var_name = kernel->getTestVariableName();
203 AddCoupledVariableNameIfMissing(trial_var_name);
204 AddTestVariableNameIfMissing(test_var_name);
205 // Register new complex kernels map if not present for the test variable
206 if (!_cmplx_kernels_map.Has(test_var_name))
207 {
208 auto kernel_field_map =
209 std::make_shared<NamedFieldsMap<std::vector<std::shared_ptr<MFEMComplexKernel>>>>();
210 _cmplx_kernels_map.Register(test_var_name, std::move(kernel_field_map));
211 }
212 // Register new complex kernels map if not present for the test/trial variable pair
213 if (!_cmplx_kernels_map.Get(test_var_name)->Has(trial_var_name))
214 {
215 auto kernels = std::make_shared<std::vector<std::shared_ptr<MFEMComplexKernel>>>();
216 _cmplx_kernels_map.Get(test_var_name)->Register(trial_var_name, std::move(kernels));
217 }
218 _cmplx_kernels_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(kernel));
219}
220
221void
222ComplexEquationSystem::AddComplexIntegratedBC(std::shared_ptr<MFEMComplexIntegratedBC> bc)
223{
224 const auto & trial_var_name = bc->getTrialVariableName();
225 const auto & test_var_name = bc->getTestVariableName();
226 AddCoupledVariableNameIfMissing(trial_var_name);
227 AddTestVariableNameIfMissing(test_var_name);
228 // Register new complex integrated bc map if not present for the test variable
229 if (!_cmplx_integrated_bc_map.Has(test_var_name))
230 {
231 auto integrated_bc_field_map =
232 std::make_shared<NamedFieldsMap<std::vector<std::shared_ptr<MFEMComplexIntegratedBC>>>>();
233 _cmplx_integrated_bc_map.Register(test_var_name, std::move(integrated_bc_field_map));
234 }
235 // Register new complex integrated bc map if not present for the test/trial variable pair
236 if (!_cmplx_integrated_bc_map.Get(test_var_name)->Has(trial_var_name))
237 {
238 auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMComplexIntegratedBC>>>();
239 _cmplx_integrated_bc_map.Get(test_var_name)->Register(trial_var_name, std::move(bcs));
240 }
241 _cmplx_integrated_bc_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(bc));
242}
243
244void
245ComplexEquationSystem::AddComplexEssentialBCs(std::shared_ptr<MFEMComplexEssentialBC> bc)
246{
247 const auto & test_var_name = bc->getTestVariableName();
248 AddTestVariableNameIfMissing(test_var_name);
249 // Register new complex essential bc map if not present for the test variable
250 if (!_cmplx_essential_bc_map.Has(test_var_name))
251 {
252 auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMComplexEssentialBC>>>();
253 _cmplx_essential_bc_map.Register(test_var_name, std::move(bcs));
254 }
255 _cmplx_essential_bc_map.GetRef(test_var_name).push_back(std::move(bc));
256}
257
258void
260{
261 for (const auto & test_var_name : _test_var_names)
262 for (const auto & eliminated_var_name : _eliminated_var_names)
263 if (_mslfs.Has(test_var_name) && _mslfs.Get(test_var_name)->Has(eliminated_var_name) &&
264 !VectorContainsName(_test_var_names, eliminated_var_name))
265 {
266 auto & mslf = *_mslfs.Get(test_var_name)->Get(eliminated_var_name);
267 auto & clf = *_clfs.Get(test_var_name);
268 const mfem::real_t scale = -1.0;
269 const mfem::real_t conv =
270 (mslf.GetConvention() == mfem::ComplexOperator::HERMITIAN) ? 1.0 : -1.0;
271
272 // y += scale * (A_r + i * A_i) * (x_r + i * x_i)
273 // and take the complex conjugate of the result if convention is BLOCK_SYMMETRIC
274 mslf.real().AddMult(
275 _cmplx_eliminated_variables.Get(eliminated_var_name)->real(), clf.real(), scale);
276 mslf.real().AddMult(
277 _cmplx_eliminated_variables.Get(eliminated_var_name)->imag(), clf.imag(), conv * scale);
278 mslf.imag().AddMult(
279 _cmplx_eliminated_variables.Get(eliminated_var_name)->imag(), clf.real(), -scale);
280 mslf.imag().AddMult(
281 _cmplx_eliminated_variables.Get(eliminated_var_name)->real(), clf.imag(), conv * scale);
282 clf.SyncAlias();
283 }
284}
285
286void
288 mfem::BlockVector & trueX,
289 mfem::BlockVector & trueRHS)
290{
291 auto & test_var_name = _test_var_names.at(0);
292 mfem::Vector aux_x, aux_rhs;
293 mfem::OperatorPtr aux_a;
294
295 auto slf = _slfs.Get(test_var_name);
296 slf->FormLinearSystem(_ess_tdof_lists.at(0),
298 *_clfs.Get(test_var_name),
299 aux_a,
300 aux_x,
301 aux_rhs,
302 /*copy_interior=*/true);
303
304 trueX.GetBlock(0) = aux_x;
305 trueRHS.GetBlock(0) = aux_rhs;
306 trueX.SyncFromBlocks();
307 trueRHS.SyncFromBlocks();
308
309 op.Reset(aux_a.Ptr());
310 aux_a.SetOperatorOwner(false);
311}
312
313void
315 mfem::BlockVector & trueX,
316 mfem::BlockVector & trueRHS)
317{
318 // Allocate block operator
320 _h_blocks.SetSize(_test_var_names.size(), _trial_var_names.size());
321 _h_blocks = nullptr;
322 // Zero out RHS and sync memory
323 trueRHS = 0.0;
324 trueRHS.SyncToBlocks();
325
326 for (const auto i : index_range(_test_var_names))
327 {
328 auto test_var_name = _test_var_names.at(i);
329
330 for (const auto j : index_range(_trial_var_names))
331 {
332 auto trial_var_name = _trial_var_names.at(j);
333
334 mfem::Vector aux_x, aux_rhs;
335 mfem::ParComplexLinearForm aux_lf(_test_pfespaces.at(i));
336 std::unique_ptr<mfem::OperatorHandle> aux_a = std::make_unique<mfem::OperatorHandle>();
337
338 if (test_var_name == trial_var_name)
339 {
340 mooseAssert(i == j, "Trial and test variables must have the same ordering.");
341 auto slf = _slfs.Get(test_var_name);
342 slf->FormLinearSystem(_ess_tdof_lists.at(j),
344 *_clfs.Get(test_var_name),
345 *aux_a,
346 aux_x,
347 aux_rhs,
348 /*copy_interior=*/true);
349 trueX.GetBlock(i) = aux_x;
350 }
351 else if (_mslfs.Has(test_var_name) && _mslfs.Get(test_var_name)->Has(trial_var_name))
352 {
353 auto mslf = _mslfs.Get(test_var_name)->Get(trial_var_name);
354 mslf->FormRectangularLinearSystem(_ess_tdof_lists.at(j),
355 _ess_tdof_lists.at(i),
357 aux_lf = 0.,
358 *aux_a,
359 aux_x,
360 aux_rhs);
361 }
362 else
363 continue;
364
365 trueRHS.GetBlock(i) += aux_rhs;
366 _h_blocks(i, j) = aux_a->As<mfem::ComplexHypreParMatrix>()->GetSystemMatrix();
367 }
368 }
369
370 // Sync memory
371 trueX.SyncFromBlocks();
372 trueRHS.SyncFromBlocks();
373
374 // Create monolithic matrix
375 op.Reset(mfem::HypreParMatrixFromBlocks(_h_blocks));
376}
377
378// Equation system Mult
379void
380ComplexEquationSystem::Mult(const mfem::Vector & x, mfem::Vector & residual) const
381{
382 _linear_operator->Mult(x, residual);
383 x.HostRead();
384 residual.HostRead();
385}
386
387void
389{
390 for (const auto i : index_range(_trial_var_names))
391 {
392 auto & trial_var_name = _trial_var_names.at(i);
393 trueX.GetBlock(i).SyncMemory(trueX);
394 _complex_gfuncs->Get(trial_var_name)->Distribute(&(trueX.GetBlock(i)));
395 }
396 // Solution variables changed: stored projections of solution-dependent coefficients are stale.
399}
400}
401
402#endif
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
Real scale
Definition MortarUtils.C:62
void markSolutionChanged()
Notify quadrature function coefficients that solution variables have changed, marking the stored valu...
void EliminateCoupledVariables() override
Perform trivial eliminations of coupled variables lacking corresponding test variables.
virtual void BuildLinearForms() override
Build linear forms and eliminate constrained DoFs.
std::vector< std::unique_ptr< mfem::ParComplexGridFunction > > _cmplx_var_ess_constraints
Complex Gridfunctions holding essential constraints from Dirichlet BCs.
Moose::MFEM::ComplexGridFunctions * _complex_gfuncs
NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMComplexKernel > > > > _cmplx_kernels_map
virtual void Mult(const mfem::Vector &x, mfem::Vector &y) const override
Nonlinear Mult (Used by Newton-solver not necessarily nonlinear)
virtual void FormSystemOperator(mfem::OperatorHandle &op, mfem::BlockVector &trueX, mfem::BlockVector &trueRHS) override
Form matrix-free representation of system operator.
virtual void BuildMixedBilinearForms() override
Build mixed bilinear forms (off-diagonal Jacobian contributions)
virtual void SetTrialVariablesFromTrueVectors(const mfem::BlockVector &trueX) const override
Update variable from solution vector after solve.
NamedFieldsMap< std::vector< std::shared_ptr< MFEMComplexEssentialBC > > > _cmplx_essential_bc_map
NamedFieldsMap< mfem::ParSesquilinearForm > _slfs
virtual void BuildBilinearForms() override
Build bilinear forms (diagonal Jacobian contributions)
virtual void Init(GridFunctions &gridfunctions, ComplexGridFunctions &cmplx_gridfunctions, mfem::AssemblyLevel assembly_level) override
Initialise.
void ApplyBoundaryLFIntegrators(const std::string &test_var_name, std::shared_ptr< mfem::ParComplexLinearForm > form, NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMComplexIntegratedBC > > > > &integrated_bc_map)
Method for applying LinearFormIntegrators on boundaries from kernels to a ParComplexLinearForm.
void AddComplexKernel(std::shared_ptr< MFEMComplexKernel > kernel)
Add complex kernels.
virtual void ApplyComplexEssentialBC(const std::string &var_name, mfem::ParComplexGridFunction &trial_gf, mfem::Array< int > &global_ess_markers)
Applies complex BCs to a single trial variable.
NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMComplexIntegratedBC > > > > _cmplx_integrated_bc_map
virtual void ApplyEssentialBCs() override
Update all essentially constrained true DoF markers and values on boundaries.
void ApplyDomainLFIntegrators(const std::string &test_var_name, std::shared_ptr< mfem::ParComplexLinearForm > form, NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMComplexKernel > > > > &kernels_map)
Method for applying LinearFormIntegrators on domains from kernels to a ParComplexLinearForm.
NamedFieldsMap< mfem::ParComplexLinearForm > _clfs
NamedFieldsMap< NamedFieldsMap< mfem::ParMixedSesquilinearForm > > _mslfs
virtual void FormSystemMatrix(mfem::OperatorHandle &op, mfem::BlockVector &trueX, mfem::BlockVector &trueRHS) override
Form matrix representation of system operator as a HypreParMatrix.
void AddComplexIntegratedBC(std::shared_ptr< MFEMComplexIntegratedBC > bc)
Add complex integrated BCs.
ComplexGridFunctions _cmplx_eliminated_variables
Pointers to coupled variables not part of the reduced ComplexEquationSystem.
void AddComplexEssentialBCs(std::shared_ptr< MFEMComplexEssentialBC > bc)
Add complex essential BCs.
virtual void AddTestVariableNameIfMissing(const std::string &test_var_name)
Add test variable to EquationSystem.
std::vector< std::string > _coupled_var_names
Names of all trial variables of kernels and boundary conditions added to this EquationSystem.
std::vector< mfem::ParFiniteElementSpace * > _test_pfespaces
Pointers to finite element spaces associated with test variables.
std::vector< mfem::ParFiniteElementSpace * > _coupled_pfespaces
Pointers to finite element spaces associated with coupled variables.
bool VectorContainsName(const std::vector< std::string > &the_vector, const std::string &name) const
mfem::Array2D< const mfem::HypreParMatrix * > _h_blocks
std::vector< std::string > _test_var_names
Names of all test variables corresponding to linear forms in this equation system.
mfem::OperatorHandle _linear_operator
mfem::AssemblyLevel _assembly_level
virtual void SetTrialVariableNames()
Set trial variable names from subset of coupled variables that have an associated test variable.
std::vector< mfem::Array< int > > _ess_tdof_lists
CoefficientManager * _coefficient_manager
void DeleteHBlocks()
Deletes the HypreParMatrix associated with any pointer stored in _h_blocks, and then proceeds to dele...
std::vector< std::string > _trial_var_names
Subset of _coupled_var_names of all variables corresponding to gridfunctions with degrees of freedom ...
std::vector< mfem::Array< int > > _ess_markers
std::vector< std::string > _eliminated_var_names
Names of all coupled variables without a corresponding test variable.
virtual void AddCoupledVariableNameIfMissing(const std::string &coupled_var_name)
Add coupled variable to EquationSystem.
void Register(const std::string &field_name, FieldArgs &&... args)
Construct new field with name field_name and register.
bool Has(const std::string &field_name) const
Predicate to check if a field is registered with name field_name.
T * Get(const std::string &field_name) const
Returns a non-owning pointer to the field. This is guaranteed to return a non-null pointer.
std::shared_ptr< T > GetShared(const std::string &field_name) const
Returns a shared pointer to the field. This is guaranteed to return a non-null shared pointer.
int size() const
Returns the number of elements in the map.
T & GetRef(const std::string &field_name) const
Returns a reference to a field.
Utilities for converting between vector(s) of libMesh Points and MFEM Vector(s).