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 // A mesh without boundary elements, such as a periodic one, has no boundary attributes
191 const auto & bdr_attributes = trial_gf.ParFESpace()->GetParMesh()->bdr_attributes;
192 _ess_markers.at(i).SetSize(bdr_attributes.Size() ? bdr_attributes.Max() : 0, 0);
193 // Set strongly constrained DoFs of trial_gf on essential boundaries and add markers for all
194 // essential boundaries to the _ess_markers array
195 ApplyComplexEssentialBC(trial_var_name, trial_gf, _ess_markers.at(i));
196 trial_gf.ParFESpace()->GetEssentialTrueDofs(_ess_markers.at(i), _ess_tdof_lists.at(i));
197 }
198}
199
200void
201ComplexEquationSystem::AddComplexKernel(std::shared_ptr<MFEMComplexKernel> kernel)
202{
203 const auto & trial_var_name = kernel->getTrialVariableName();
204 const auto & test_var_name = kernel->getTestVariableName();
205 AddCoupledVariableNameIfMissing(trial_var_name);
206 AddTestVariableNameIfMissing(test_var_name);
207 // Register new complex kernels map if not present for the test variable
208 if (!_cmplx_kernels_map.Has(test_var_name))
209 {
210 auto kernel_field_map =
211 std::make_shared<NamedFieldsMap<std::vector<std::shared_ptr<MFEMComplexKernel>>>>();
212 _cmplx_kernels_map.Register(test_var_name, std::move(kernel_field_map));
213 }
214 // Register new complex kernels map if not present for the test/trial variable pair
215 if (!_cmplx_kernels_map.Get(test_var_name)->Has(trial_var_name))
216 {
217 auto kernels = std::make_shared<std::vector<std::shared_ptr<MFEMComplexKernel>>>();
218 _cmplx_kernels_map.Get(test_var_name)->Register(trial_var_name, std::move(kernels));
219 }
220 _cmplx_kernels_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(kernel));
221}
222
223void
224ComplexEquationSystem::AddComplexIntegratedBC(std::shared_ptr<MFEMComplexIntegratedBC> bc)
225{
226 const auto & trial_var_name = bc->getTrialVariableName();
227 const auto & test_var_name = bc->getTestVariableName();
228 AddCoupledVariableNameIfMissing(trial_var_name);
229 AddTestVariableNameIfMissing(test_var_name);
230 // Register new complex integrated bc map if not present for the test variable
231 if (!_cmplx_integrated_bc_map.Has(test_var_name))
232 {
233 auto integrated_bc_field_map =
234 std::make_shared<NamedFieldsMap<std::vector<std::shared_ptr<MFEMComplexIntegratedBC>>>>();
235 _cmplx_integrated_bc_map.Register(test_var_name, std::move(integrated_bc_field_map));
236 }
237 // Register new complex integrated bc map if not present for the test/trial variable pair
238 if (!_cmplx_integrated_bc_map.Get(test_var_name)->Has(trial_var_name))
239 {
240 auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMComplexIntegratedBC>>>();
241 _cmplx_integrated_bc_map.Get(test_var_name)->Register(trial_var_name, std::move(bcs));
242 }
243 _cmplx_integrated_bc_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(bc));
244}
245
246void
247ComplexEquationSystem::AddComplexEssentialBCs(std::shared_ptr<MFEMComplexEssentialBC> bc)
248{
249 const auto & test_var_name = bc->getTestVariableName();
250 AddTestVariableNameIfMissing(test_var_name);
251 // Register new complex essential bc map if not present for the test variable
252 if (!_cmplx_essential_bc_map.Has(test_var_name))
253 {
254 auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMComplexEssentialBC>>>();
255 _cmplx_essential_bc_map.Register(test_var_name, std::move(bcs));
256 }
257 _cmplx_essential_bc_map.GetRef(test_var_name).push_back(std::move(bc));
258}
259
260void
262{
263 for (const auto & test_var_name : _test_var_names)
264 for (const auto & eliminated_var_name : _eliminated_var_names)
265 if (_mslfs.Has(test_var_name) && _mslfs.Get(test_var_name)->Has(eliminated_var_name) &&
266 !VectorContainsName(_test_var_names, eliminated_var_name))
267 {
268 auto & mslf = *_mslfs.Get(test_var_name)->Get(eliminated_var_name);
269 auto & clf = *_clfs.Get(test_var_name);
270 const mfem::real_t scale = -1.0;
271 const mfem::real_t conv =
272 (mslf.GetConvention() == mfem::ComplexOperator::HERMITIAN) ? 1.0 : -1.0;
273
274 // y += scale * (A_r + i * A_i) * (x_r + i * x_i)
275 // and take the complex conjugate of the result if convention is BLOCK_SYMMETRIC
276 mslf.real().AddMult(
277 _cmplx_eliminated_variables.Get(eliminated_var_name)->real(), clf.real(), scale);
278 mslf.real().AddMult(
279 _cmplx_eliminated_variables.Get(eliminated_var_name)->imag(), clf.imag(), conv * scale);
280 mslf.imag().AddMult(
281 _cmplx_eliminated_variables.Get(eliminated_var_name)->imag(), clf.real(), -scale);
282 mslf.imag().AddMult(
283 _cmplx_eliminated_variables.Get(eliminated_var_name)->real(), clf.imag(), conv * scale);
284 clf.SyncAlias();
285 }
286}
287
288void
290 mfem::BlockVector & trueX,
291 mfem::BlockVector & trueRHS)
292{
293 auto & test_var_name = _test_var_names.at(0);
294 mfem::Vector aux_x, aux_rhs;
295 mfem::OperatorPtr aux_a;
296
297 auto slf = _slfs.Get(test_var_name);
298 slf->FormLinearSystem(_ess_tdof_lists.at(0),
300 *_clfs.Get(test_var_name),
301 aux_a,
302 aux_x,
303 aux_rhs,
304 /*copy_interior=*/true);
305
306 trueX.GetBlock(0) = aux_x;
307 trueRHS.GetBlock(0) = aux_rhs;
308 trueX.SyncFromBlocks();
309 trueRHS.SyncFromBlocks();
310
311 op.Reset(aux_a.Ptr());
312 aux_a.SetOperatorOwner(false);
313}
314
315void
317 mfem::BlockVector & trueX,
318 mfem::BlockVector & trueRHS)
319{
320 // Allocate block operator
322 _h_blocks.SetSize(_test_var_names.size(), _trial_var_names.size());
323 _h_blocks = nullptr;
324 // Zero out RHS and sync memory
325 trueRHS = 0.0;
326 trueRHS.SyncToBlocks();
327
328 for (const auto i : index_range(_test_var_names))
329 {
330 auto test_var_name = _test_var_names.at(i);
331
332 for (const auto j : index_range(_trial_var_names))
333 {
334 auto trial_var_name = _trial_var_names.at(j);
335
336 mfem::Vector aux_x, aux_rhs;
337 mfem::ParComplexLinearForm aux_lf(_test_pfespaces.at(i));
338 std::unique_ptr<mfem::OperatorHandle> aux_a = std::make_unique<mfem::OperatorHandle>();
339
340 if (test_var_name == trial_var_name)
341 {
342 mooseAssert(i == j, "Trial and test variables must have the same ordering.");
343 auto slf = _slfs.Get(test_var_name);
344 slf->FormLinearSystem(_ess_tdof_lists.at(j),
346 *_clfs.Get(test_var_name),
347 *aux_a,
348 aux_x,
349 aux_rhs,
350 /*copy_interior=*/true);
351 trueX.GetBlock(i) = aux_x;
352 }
353 else if (_mslfs.Has(test_var_name) && _mslfs.Get(test_var_name)->Has(trial_var_name))
354 {
355 auto mslf = _mslfs.Get(test_var_name)->Get(trial_var_name);
356 mslf->FormRectangularLinearSystem(_ess_tdof_lists.at(j),
357 _ess_tdof_lists.at(i),
359 aux_lf = 0.,
360 *aux_a,
361 aux_x,
362 aux_rhs);
363 }
364 else
365 continue;
366
367 trueRHS.GetBlock(i) += aux_rhs;
368 _h_blocks(i, j) = aux_a->As<mfem::ComplexHypreParMatrix>()->GetSystemMatrix();
369 }
370 }
371
372 // Sync memory
373 trueX.SyncFromBlocks();
374 trueRHS.SyncFromBlocks();
375
376 // Create monolithic matrix
377 op.Reset(mfem::HypreParMatrixFromBlocks(_h_blocks));
378}
379
380// Equation system Mult
381void
382ComplexEquationSystem::Mult(const mfem::Vector & x, mfem::Vector & residual) const
383{
384 _linear_operator->Mult(x, residual);
385 x.HostRead();
386 residual.HostRead();
387}
388
389void
391{
392 for (const auto i : index_range(_trial_var_names))
393 {
394 auto & trial_var_name = _trial_var_names.at(i);
395 trueX.GetBlock(i).SyncMemory(trueX);
396 _complex_gfuncs->Get(trial_var_name)->Distribute(&(trueX.GetBlock(i)));
397 }
398 // Solution variables changed: stored projections of solution-dependent coefficients are stale.
401}
402}
403
404#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).