https://mooseframework.inl.gov
Loading...
Searching...
No Matches
EquationSystem.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
12#include "EquationSystem.h"
14#include "CoefficientManager.h"
15#include "libmesh/int_range.h"
16
17namespace Moose::MFEM
18{
19
25
26void
28{
29 for (const auto i : make_range(_h_blocks.NumRows()))
30 for (const auto j : make_range(_h_blocks.NumCols()))
31 {
32 if (_jacobian_blocks.NumRows() && _jacobian_blocks(i, j) == _h_blocks(i, j))
33 _jacobian_blocks(i, j) = nullptr;
34 delete _h_blocks(i, j);
35 }
36 _h_blocks.DeleteAll();
37}
38
39void
41{
42 for (const auto i : make_range(_jacobian_blocks.NumRows()))
43 for (const auto j : make_range(_jacobian_blocks.NumCols()))
44 if (!_h_blocks.NumRows() || _jacobian_blocks(i, j) != _h_blocks(i, j))
45 delete _jacobian_blocks(i, j);
46 _jacobian_blocks.DeleteAll();
47}
48
49bool
50EquationSystem::VectorContainsName(const std::vector<std::string> & the_vector,
51 const std::string & name) const
52{
53 return std::find(the_vector.begin(), the_vector.end(), name) != the_vector.end();
54}
55
56void
57EquationSystem::AddCoupledVariableNameIfMissing(const std::string & coupled_var_name)
58{
59 if (!VectorContainsName(_coupled_var_names, coupled_var_name))
60 _coupled_var_names.push_back(coupled_var_name);
61}
62
63void
64EquationSystem::AddEliminatedVariableNameIfMissing(const std::string & eliminated_var_name)
65{
66 if (!VectorContainsName(_eliminated_var_names, eliminated_var_name))
67 _eliminated_var_names.push_back(eliminated_var_name);
68}
69
70void
71EquationSystem::AddTestVariableNameIfMissing(const std::string & test_var_name)
72{
73 if (!VectorContainsName(_test_var_names, test_var_name))
74 _test_var_names.push_back(test_var_name);
75}
76
77void
79{
80 // If a coupled variable has an equation associated with it,
81 // add it to the set of trial variables.
82 for (const auto & test_var_name : _test_var_names)
83 if (VectorContainsName(_coupled_var_names, test_var_name))
84 _trial_var_names.push_back(test_var_name);
85
86 // Otherwise, add it to the set of eliminated variables.
87 for (const auto & coupled_var_name : _coupled_var_names)
88 if (!VectorContainsName(_test_var_names, coupled_var_name))
89 _eliminated_var_names.push_back(coupled_var_name);
90}
91
92void
93EquationSystem::AddKernel(std::shared_ptr<MFEMKernel> kernel)
94{
95 const auto & trial_var_name = kernel->getTrialVariableName();
96 const auto & test_var_name = kernel->getTestVariableName();
97 AddCoupledVariableNameIfMissing(trial_var_name);
98 AddTestVariableNameIfMissing(test_var_name);
99 // Register new kernels map if not present for the test variable
100 if (!_kernels_map.Has(test_var_name))
101 {
102 auto kernel_field_map =
103 std::make_shared<Moose::MFEM::NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>>();
104 _kernels_map.Register(test_var_name, std::move(kernel_field_map));
105 }
106 // Register new kernels map if not present for the test/trial variable pair
107 if (!_kernels_map.Get(test_var_name)->Has(trial_var_name))
108 {
109 auto kernels = std::make_shared<std::vector<std::shared_ptr<MFEMKernel>>>();
110 _kernels_map.Get(test_var_name)->Register(trial_var_name, std::move(kernels));
111 }
112 _kernels_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(kernel));
113}
114
115void
116EquationSystem::AddIntegratedBC(std::shared_ptr<MFEMIntegratedBC> bc)
117{
118 const auto & trial_var_name = bc->getTrialVariableName();
119 const auto & test_var_name = bc->getTestVariableName();
120 AddCoupledVariableNameIfMissing(trial_var_name);
121 AddTestVariableNameIfMissing(test_var_name);
122 // Register new integrated bc map if not present for the test variable
123 if (!_integrated_bc_map.Has(test_var_name))
124 {
125 auto integrated_bc_field_map = std::make_shared<
127 _integrated_bc_map.Register(test_var_name, std::move(integrated_bc_field_map));
128 }
129 // Register new integrated bc map if not present for the test/trial variable pair
130 if (!_integrated_bc_map.Get(test_var_name)->Has(trial_var_name))
131 {
132 auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMIntegratedBC>>>();
133 _integrated_bc_map.Get(test_var_name)->Register(trial_var_name, std::move(bcs));
134 }
135 _integrated_bc_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(bc));
136}
137
138void
139EquationSystem::AddEssentialBC(std::shared_ptr<MFEMEssentialBC> bc)
140{
141 const auto & test_var_name = bc->getTestVariableName();
142 AddTestVariableNameIfMissing(test_var_name);
143 // Register new essential bc map if not present for the test variable
144 if (!_essential_bc_map.Has(test_var_name))
145 {
146 auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMEssentialBC>>>();
147 _essential_bc_map.Register(test_var_name, std::move(bcs));
148 }
149 _essential_bc_map.GetRef(test_var_name).push_back(std::move(bc));
150}
151
152void
154 Moose::MFEM::ComplexGridFunctions & /*cmplx_gridfunctions*/,
155 mfem::AssemblyLevel assembly_level)
156{
157 _assembly_level = assembly_level;
158
159 // Extract which coupled variables are to be trivially eliminated and which are trial variables
161
162 for (auto & test_var_name : _test_var_names)
163 {
164 if (!gridfunctions.Has(test_var_name))
165 {
166 mooseError("MFEM variable ",
167 test_var_name,
168 " requested by equation system during initialization was "
169 "not found in gridfunctions");
170 }
171 // Store pointers to test FESpaces
172 _test_pfespaces.push_back(gridfunctions.Get(test_var_name)->ParFESpace());
173 }
174
175 for (auto & trial_var_name : _trial_var_names)
176 {
177 if (!gridfunctions.Has(trial_var_name))
178 {
179 mooseError("MFEM variable ",
180 trial_var_name,
181 " requested by equation system during initialization was "
182 "not found in gridfunctions");
183 }
184 // Create auxiliary gridfunctions for storing essential constraints from Dirichlet conditions
185 _var_ess_constraints.emplace_back(
186 std::make_unique<mfem::ParGridFunction>(gridfunctions.Get(trial_var_name)->ParFESpace()));
187 }
188
189 // Store pointers to FESpaces of all coupled variables
190 for (auto & coupled_var_name : _coupled_var_names)
191 _coupled_pfespaces.push_back(gridfunctions.Get(coupled_var_name)->ParFESpace());
192
193 // Store pointers to coupled variable GridFunctions that are to be eliminated prior to forming the
194 // jacobian
195 for (auto & eliminated_var_name : _eliminated_var_names)
196 _eliminated_variables.Register(eliminated_var_name,
197 gridfunctions.GetShared(eliminated_var_name));
198
199 // Get a reference to the GridFunctions
200 _gfuncs = &gridfunctions;
201}
202
203void
204EquationSystem::ApplyEssentialBC(const std::string & var_name,
205 mfem::ParGridFunction & trial_gf,
206 mfem::Array<int> & global_ess_markers)
207{
208 if (_essential_bc_map.Has(var_name))
209 for (auto & bc : _essential_bc_map.GetRef(var_name))
210 {
211 // Set constrained DoFs values on essential boundaries
212 bc->ApplyBC(trial_gf);
213 // Fetch marker array labelling essential boundaries of current BC
214 mfem::Array<int> ess_bdrs(bc->getBoundaryMarkers());
215 // Add these boundary markers to the set of markers labelling all essential boundaries
216 for (const auto i : make_range(ess_bdrs.Size()))
217 global_ess_markers[i] |= ess_bdrs[i];
218 }
219}
220
221void
223{
224 _ess_tdof_lists.resize(_trial_var_names.size());
225 _ess_markers.resize(_trial_var_names.size());
226 for (const auto i : index_range(_trial_var_names))
227 {
228 const auto & trial_var_name = _trial_var_names.at(i);
229 mfem::ParGridFunction & trial_gf = *_var_ess_constraints.at(i);
230
231 // Make sure we update the size, if this mesh has changed recently for instance
232 trial_gf.Update();
233
234 // Initial guess for iterative solvers (initial condition or the previous time step solution)
235 trial_gf = _gfuncs->GetRef(trial_var_name);
236
237 // A mesh without boundary elements, such as a periodic one, has no boundary attributes
238 const auto & bdr_attributes = trial_gf.ParFESpace()->GetParMesh()->bdr_attributes;
239 _ess_markers.at(i).SetSize(bdr_attributes.Size() ? bdr_attributes.Max() : 0, 0);
240 // Set strongly constrained DoFs of trial_gf on essential boundaries and add markers for all
241 // essential boundaries to the _ess_markers array
242 ApplyEssentialBC(trial_var_name, trial_gf, _ess_markers.at(i));
243 trial_gf.ParFESpace()->GetEssentialTrueDofs(_ess_markers.at(i), _ess_tdof_lists.at(i));
244 }
245}
246
247void
249{
250 for (const auto & test_var_name : _test_var_names)
251 for (const auto & eliminated_var_name : _eliminated_var_names)
252 if (_mblfs.Has(test_var_name) && _mblfs.Get(test_var_name)->Has(eliminated_var_name) &&
253 !VectorContainsName(_test_var_names, eliminated_var_name))
254 {
255 auto & mblf = *_mblfs.Get(test_var_name)->Get(eliminated_var_name);
256 mblf.AddMult(*_eliminated_variables.Get(eliminated_var_name), *_lfs.Get(test_var_name), -1);
257 }
258}
259
260void
261EquationSystem::FormLinearSystem(mfem::OperatorHandle & op,
262 mfem::BlockVector & trueX,
263 mfem::BlockVector & trueRHS)
264{
265 mooseAssert(_test_var_names.size() == _trial_var_names.size(),
266 "Number of test and trial variables must be the same for block matrix assembly.");
267
268 if (_assembly_level == mfem::AssemblyLevel::LEGACY)
269 FormSystemMatrix(op, trueX, trueRHS);
270 else
271 FormSystemOperator(op, trueX, trueRHS);
272}
273
274void
275EquationSystem::FormSystemOperator(mfem::OperatorHandle & op,
276 mfem::BlockVector & trueX,
277 mfem::BlockVector & trueRHS)
278{
279 mooseAssert(_test_var_names.size() == 1 && _test_var_names.size() == _trial_var_names.size(),
280 "Non-legacy assembly is only supported for single test and trial variable systems");
281
282 auto & test_var_name = _test_var_names.at(0);
283 mfem::Vector aux_x, aux_rhs;
284 mfem::OperatorPtr aux_a;
285
286 auto blf = _blfs.Get(test_var_name);
287 blf->FormLinearSystem(_ess_tdof_lists.at(0),
289 *_lfs.Get(test_var_name),
290 aux_a,
291 aux_x,
292 aux_rhs,
293 /*copy_interior=*/true);
294
295 trueX.GetBlock(0) = aux_x;
296 trueRHS.GetBlock(0) = aux_rhs;
297 trueX.SyncFromBlocks();
298 trueRHS.SyncFromBlocks();
299
300 op.Reset(aux_a.Ptr());
301 aux_a.SetOperatorOwner(false);
302}
303
304void
305EquationSystem::FormSystemMatrix(mfem::OperatorHandle & op,
306 mfem::BlockVector & trueX,
307 mfem::BlockVector & trueRHS)
308{
309 // Allocate block operator
311 _h_blocks.SetSize(_test_var_names.size(), _trial_var_names.size());
312 _h_blocks = nullptr;
313 // Zero out RHS and sync memory
314 trueRHS = 0.0;
315 trueRHS.SyncToBlocks();
316
317 for (const auto i : index_range(_test_var_names))
318 {
319 auto test_var_name = _test_var_names.at(i);
320
321 for (const auto j : index_range(_trial_var_names))
322 {
323 auto trial_var_name = _trial_var_names.at(j);
324
325 mfem::Vector aux_x, aux_rhs;
326 mfem::ParLinearForm aux_lf(_test_pfespaces.at(i));
327 mfem::HypreParMatrix * aux_a = new mfem::HypreParMatrix;
328
329 if (test_var_name == trial_var_name)
330 {
331 mooseAssert(i == j, "Trial and test variables must have the same ordering.");
332 auto blf = _blfs.Get(test_var_name);
333 blf->FormLinearSystem(_ess_tdof_lists.at(j),
335 *_lfs.Get(test_var_name),
336 *aux_a,
337 aux_x,
338 aux_rhs,
339 /*copy_interior=*/true);
340 trueX.GetBlock(j) = aux_x;
341 }
342 else if (_mblfs.Has(test_var_name) && _mblfs.Get(test_var_name)->Has(trial_var_name))
343 {
344 auto mblf = _mblfs.Get(test_var_name)->Get(trial_var_name);
345 mblf->FormRectangularLinearSystem(_ess_tdof_lists.at(j),
346 _ess_tdof_lists.at(i),
348 aux_lf = 0.,
349 *aux_a,
350 aux_x,
351 aux_rhs);
352 }
353 else
354 continue;
355
356 trueRHS.GetBlock(i) += aux_rhs;
357 _h_blocks(i, j) = aux_a;
358 }
359 }
360 // Sync memory
361 trueX.SyncFromBlocks();
362 trueRHS.SyncFromBlocks();
363
364 // Create monolithic matrix
365 op.Reset(mfem::HypreParMatrixFromBlocks(_h_blocks));
366}
367
368void
369EquationSystem::FormSystem(mfem::BlockVector & trueX, mfem::BlockVector & trueRHS)
370{
372 height = trueX.Size();
373 width = trueRHS.Size();
374 // Store block offsets
375 _block_true_offsets.SetSize(trueX.NumBlocks() + 1);
376 _block_true_offsets[0] = 0;
377 for (unsigned i = 0; i < _trial_var_names.size(); i++)
378 _block_true_offsets[i + 1] = trueX.BlockSize(i);
379 _block_true_offsets.PartialSum();
380 FormLinearSystem(_linear_operator, trueX, trueRHS);
381}
382
383void
384EquationSystem::Mult(const mfem::Vector & sol, mfem::Vector & residual) const
385{
386 _linear_operator->Mult(sol, residual);
387
388 if (IsNonlinear())
389 ComputeNonlinearResidual(sol, residual);
390}
391
392void
393EquationSystem::ComputeNonlinearResidual(const mfem::Vector & sol, mfem::Vector & residual) const
394{
395 mooseAssert(IsNonlinear(), "Should not be calling this method if our forms are not nonlinear");
396
397 const mfem::BlockVector block_solution(const_cast<mfem::Vector &>(sol), _block_true_offsets);
398 SetTrialVariablesFromTrueVectors(block_solution);
399
400 mfem::BlockVector block_residual(residual, _block_true_offsets);
401 for (unsigned int i = 0; i < _test_var_names.size(); i++)
402 {
403 auto & test_var_name = _test_var_names.at(i);
404 auto nlf = _nlfs.GetShared(test_var_name);
405 nlf->AddMult(block_solution.GetBlock(i), block_residual.GetBlock(i));
406 block_residual.GetBlock(i).SyncAliasMemory(block_residual);
407 }
408}
409
410void
412{
414 _jacobian_blocks.SetSize(_test_var_names.size(), _trial_var_names.size());
415 _jacobian_blocks = nullptr;
416
417 const mfem::BlockVector update_vector(const_cast<mfem::Vector &>(u), _block_true_offsets);
418 for (const auto i : index_range(_test_var_names))
419 {
420 auto test_var_name = _test_var_names.at(i);
421 if (_nlfs.Has(test_var_name))
422 {
423 auto nlf = _nlfs.Get(test_var_name);
424 mfem::HypreParMatrix * nlf_jac =
425 dynamic_cast<mfem::HypreParMatrix *>(&nlf->GetGradient(update_vector.GetBlock(i)));
426 mooseAssert(nlf_jac,
427 "Jacobian contribution of nonlinear form associated with " + test_var_name +
428 " is not castable into a HypreParMatrix");
429 _jacobian_blocks(i, i) = mfem::ParAdd(_h_blocks(i, i), nlf_jac);
430 }
431 else
432 _jacobian_blocks(i, i) = _h_blocks(i, i);
433 for (const auto j : index_range(_trial_var_names))
434 if (i != j) // nlf->GetGradient only contributes to on-diagonal blocks
435 _jacobian_blocks(i, j) = _h_blocks(i, j);
436 }
437 // Create monolithic matrix
438 _jacobian.Reset(mfem::HypreParMatrixFromBlocks(_jacobian_blocks));
439}
440
441mfem::Operator &
442EquationSystem::GetGradient(const mfem::Vector & u) const
443{
445
446 if (IsNonlinear())
447 {
448 if (_assembly_level != mfem::AssemblyLevel::LEGACY)
449 mooseError("MFEM nonlinear solvers that require GetGradient() currently require legacy "
450 "assembly in EquationSystem.");
451 const_cast<EquationSystem *>(this)->FormJacobianMatrix(u);
452 }
453 else
455
456 return *_jacobian;
457}
458
459void
460EquationSystem::SetTrialVariablesFromTrueVectors(const mfem::BlockVector & trueX) const
461{
462 for (const auto i : index_range(_trial_var_names))
463 {
464 auto & trial_var_name = _trial_var_names.at(i);
465 trueX.GetBlock(i).SyncMemory(trueX);
466 _gfuncs->Get(trial_var_name)->Distribute(&(trueX.GetBlock(i)));
467 }
468 // Solution variables changed: stored projections of solution-dependent coefficients are stale.
471}
472
473void
475{
476 // Register linear forms
477 for (const auto i : index_range(_test_var_names))
478 {
479 auto test_var_name = _test_var_names.at(i);
480 _lfs.Register(test_var_name, std::make_shared<mfem::ParLinearForm>(_test_pfespaces.at(i)));
481 _lfs.GetRef(test_var_name) = 0.0;
482 }
483
484 for (auto & test_var_name : _test_var_names)
485 {
486 // Apply kernels
487 auto lf = _lfs.GetShared(test_var_name);
488 ApplyDomainLFIntegrators(test_var_name, lf, _kernels_map);
490 lf->Assemble();
491 }
492
493 // Apply essential boundary conditions
495
496 // Eliminate trivially eliminated variables by subtracting contributions from linear forms
498}
499
500void
502{
503 // Register non-linear Action forms
504 for (const auto i : index_range(_test_var_names))
505 {
506 auto test_var_name = _test_var_names.at(i);
507 _nlfs.Register(test_var_name, std::make_shared<mfem::ParNonlinearForm>(_test_pfespaces.at(i)));
508 // Apply kernels
509 auto nlf = _nlfs.GetShared(test_var_name);
510 nlf->SetEssentialTrueDofs(_ess_tdof_lists.at(i));
511 ApplyDomainNLFIntegrators(test_var_name, nlf, _kernels_map, std::nullopt);
512 ApplyBoundaryNLFIntegrators(test_var_name, nlf, _integrated_bc_map, std::nullopt);
513 }
514}
515
516void
518{
519 // Register bilinear forms
520 for (const auto i : index_range(_test_var_names))
521 {
522 auto test_var_name = _test_var_names.at(i);
523 _blfs.Register(test_var_name, std::make_shared<mfem::ParBilinearForm>(_test_pfespaces.at(i)));
524
525 // Apply kernels
526 auto blf = _blfs.GetShared(test_var_name);
527 blf->SetAssemblyLevel(_assembly_level);
528 ApplyBoundaryBLFIntegrators<mfem::ParBilinearForm>(
529 test_var_name, test_var_name, blf, _integrated_bc_map);
530 ApplyDomainBLFIntegrators<mfem::ParBilinearForm>(
531 test_var_name, test_var_name, blf, _kernels_map);
532 // Assemble
533 blf->Assemble();
534 blf->Finalize();
535 }
536}
537
538void
540{
541 // Register mixed bilinear forms. Note that not all combinations may
542 // have a kernel.
543
544 // Create mblf for each test/coupled variable pair with an added kernel.
545 // Mixed bilinear forms with coupled variables that are not trial variables are
546 // associated with contributions from eliminated variables.
547 for (const auto i : index_range(_test_var_names))
548 {
549 auto test_var_name = _test_var_names.at(i);
550 auto test_mblfs = std::make_shared<Moose::MFEM::NamedFieldsMap<mfem::ParMixedBilinearForm>>();
551 for (const auto j : index_range(_coupled_var_names))
552 {
553 const auto & coupled_var_name = _coupled_var_names.at(j);
554 auto mblf = std::make_shared<mfem::ParMixedBilinearForm>(_coupled_pfespaces.at(j),
555 _test_pfespaces.at(i));
556 // Register MixedBilinearForm if kernels exist for it, and assemble kernels
557 if (_kernels_map.Has(test_var_name) &&
558 _kernels_map.Get(test_var_name)->Has(coupled_var_name) &&
559 test_var_name != coupled_var_name)
560 {
561 mblf->SetAssemblyLevel(_assembly_level);
562 // Apply all mixed kernels with this test/trial pair
563 ApplyDomainBLFIntegrators<mfem::ParMixedBilinearForm>(
564 coupled_var_name, test_var_name, mblf, _kernels_map);
565 // Assemble mixed bilinear forms
566 mblf->Assemble();
567 mblf->Finalize();
568 // Register mixed bilinear forms associated with a single trial variable
569 // for the current test variable
570 test_mblfs->Register(coupled_var_name, mblf);
571 }
572 }
573 // Register all mixed bilinear form sets associated with a single test variable
574 _mblfs.Register(test_var_name, test_mblfs);
575 }
576}
577
578void
586
587void
589 const std::string & test_var_name,
590 std::shared_ptr<mfem::ParLinearForm> form,
591 NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>> & kernels_map)
592{
593 if (kernels_map.Has(test_var_name) && kernels_map.Get(test_var_name)->Has(test_var_name))
594 {
595 auto kernels = kernels_map.GetRef(test_var_name).GetRef(test_var_name);
596 for (auto & kernel : kernels)
597 {
598 mfem::LinearFormIntegrator * integ = kernel->createLFIntegrator();
599
600 if (integ)
601 {
602 kernel->isSubdomainRestricted()
603 ? form->AddDomainIntegrator(std::move(integ), kernel->getSubdomainMarkers())
604 : form->AddDomainIntegrator(std::move(integ));
605 }
606 }
607 }
608}
609
610void
612 const std::string & test_var_name,
613 std::shared_ptr<mfem::ParNonlinearForm> form,
614 NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>> & kernels_map,
615 std::optional<mfem::real_t> scale_factor)
616{
617 if (kernels_map.Has(test_var_name))
618 for (const auto & [trial_var_name, kernels] : kernels_map.GetRef(test_var_name))
619 for (auto & kernel : *kernels)
620 if (auto * integ = kernel->createNLIntegrator())
621 {
622 if (_gradient_required && (trial_var_name != test_var_name))
623 mooseError("Support for off-diagonal MFEM nonlinear domain integrators in conjunction "
624 "with a nonlinear solver that requires a gradient is not currently "
625 "implemented. Kernel '",
626 kernel->name(),
627 "' contributes to test variable '",
628 test_var_name,
629 "' from trial variable '",
630 trial_var_name,
631 "'.");
632
633 _non_linear = true;
634 if (scale_factor.has_value())
635 integ = new NLScaleIntegrator(integ, scale_factor.value(), true);
636 kernel->isSubdomainRestricted()
637 ? form->AddDomainIntegrator(std::move(integ), kernel->getSubdomainMarkers())
638 : form->AddDomainIntegrator(std::move(integ));
639 }
640}
641
642void
644 const std::string & test_var_name,
645 std::shared_ptr<mfem::ParLinearForm> form,
646 NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMIntegratedBC>>>> &
647 integrated_bc_map)
648{
649 if (integrated_bc_map.Has(test_var_name) &&
650 integrated_bc_map.Get(test_var_name)->Has(test_var_name))
651 {
652 auto bcs = integrated_bc_map.GetRef(test_var_name).GetRef(test_var_name);
653 for (auto & bc : bcs)
654 {
655 mfem::LinearFormIntegrator * integ = bc->createLFIntegrator();
656
657 if (integ)
658 {
659 bc->isDGBC() ? bc->isBoundaryRestricted()
660 ? form->AddBdrFaceIntegrator(std::move(integ), bc->getBoundaryMarkers())
661 : form->AddBdrFaceIntegrator(std::move(integ))
662 : bc->isBoundaryRestricted()
663 ? form->AddBoundaryIntegrator(std::move(integ), bc->getBoundaryMarkers())
664 : form->AddBoundaryIntegrator(std::move(integ));
665 }
666 }
667 }
668}
669
670void
672 const std::string & test_var_name,
673 std::shared_ptr<mfem::ParNonlinearForm> form,
674 NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMIntegratedBC>>>> &
675 integrated_bc_map,
676 std::optional<mfem::real_t> scale_factor)
677{
678 if (integrated_bc_map.Has(test_var_name))
679 for (const auto & [trial_var_name, bcs] : integrated_bc_map.GetRef(test_var_name))
680 for (auto & bc : *bcs)
681 if (auto * integ = bc->createNLIntegrator())
682 {
683 if (_gradient_required && (test_var_name != trial_var_name))
685 "Support for Off-diagonal MFEM nonlinear boundary integrators in conjunction with "
686 "a nonlinear solver that requires a gradient is not currently "
687 "implemented. Boundary condition '",
688 bc->name(),
689 "' contributes to test variable '",
690 test_var_name,
691 "' from trial variable '",
692 trial_var_name,
693 "'.");
694
695 _non_linear = true;
696 if (scale_factor.has_value())
697 integ = new NLScaleIntegrator(integ, scale_factor.value(), true);
698 bc->isBoundaryRestricted()
699 ? form->AddBoundaryIntegrator(std::move(integ), bc->getBoundaryMarkers())
700 : form->AddBoundaryIntegrator(std::move(integ));
701 }
702}
703
704const mfem::Vector &
706{
708 mooseError("EquationSystem::GetLinearizationPoint() called before GetGradient().");
709 return *_linearization_point;
710}
711
712std::shared_ptr<mfem::ParBilinearForm>
714 mfem::ParFiniteElementSpace & fespace,
715 mfem::AssemblyLevel assembly_level)
716{
717 auto blf = std::make_shared<mfem::ParBilinearForm>(&fespace);
718 blf->SetAssemblyLevel(assembly_level);
719 ApplyBoundaryBLFIntegrators<mfem::ParBilinearForm>(var_name, var_name, blf, _integrated_bc_map);
720 ApplyDomainBLFIntegrators<mfem::ParBilinearForm>(var_name, var_name, blf, _kernels_map);
721 blf->Assemble();
722 blf->Finalize();
723 return blf;
724}
725
726std::shared_ptr<mfem::ParNonlinearForm>
728 mfem::ParFiniteElementSpace & fespace,
729 mfem::AssemblyLevel /*assembly_level*/)
730{
731 auto nlf = std::make_shared<mfem::ParNonlinearForm>(&fespace);
732 ApplyDomainNLFIntegrators(var_name, nlf, _kernels_map, std::nullopt);
733 ApplyBoundaryNLFIntegrators(var_name, nlf, _integrated_bc_map, std::nullopt);
734 return nlf;
735}
736
737mfem::Array<int> &
739{
740 for (const auto i : index_range(_trial_var_names))
741 if (_trial_var_names.at(i) == var_name)
742 return _ess_markers.at(i);
743
744 mooseError("No essential boundary markers found for variable '", var_name, "'.");
745}
746
747} // namespace Moose::MFEM
748
749#endif
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
void markSolutionChanged()
Notify quadrature function coefficients that solution variables have changed, marking the stored valu...
Owns the weak-form mathematics of a MOOSE MFEM problem.
virtual void AddTestVariableNameIfMissing(const std::string &test_var_name)
Add test variable to EquationSystem.
std::shared_ptr< mfem::ParBilinearForm > BuildBilinearFormForFESpace(const std::string &var_name, mfem::ParFiniteElementSpace &fespace, mfem::AssemblyLevel assembly_level)
Build a fresh ParBilinearForm on the given FESpace using the same kernels as the main system's biline...
virtual void BuildBilinearForms()
Build bilinear forms (diagonal Jacobian contributions)
virtual void BuildNonlinearForms()
Build non-linear action forms.
std::vector< std::string > _coupled_var_names
Names of all trial variables of kernels and boundary conditions added to this EquationSystem.
virtual void ApplyEssentialBCs()
Update all essentially constrained true DoF markers and values on boundaries.
std::vector< mfem::ParFiniteElementSpace * > _test_pfespaces
Pointers to finite element spaces associated with test variables.
mfem::Array< int > _block_true_offsets
void FormSystem(mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
Build all weak-form components via BuildEquationSystem(), form the constrained linear part of the sys...
NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMKernel > > > > _kernels_map
Arrays to store kernels to act on each component of weak form.
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
void ApplyDomainNLFIntegrators(const std::string &test_var_name, std::shared_ptr< mfem::ParNonlinearForm > form, NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMKernel > > > > &kernels_map, std::optional< mfem::real_t > scale_factor=std::nullopt)
Apply domain NonlinearFormIntegrators from kernels to the nonlinear form associated with the supplied...
virtual void ApplyEssentialBC(const std::string &var_name, mfem::ParGridFunction &trial_gf, mfem::Array< int > &global_ess_markers)
Apply essential BC(s) associated with var_name to set true DoFs of trial_gf and update markers of all...
virtual void EliminateCoupledVariables()
Perform trivial eliminations of coupled variables lacking corresponding test variables.
virtual void BuildEquationSystem()
Build all forms comprising this EquationSystem.
mfem::Operator & GetGradient(const mfem::Vector &u) const override
Get Jacobian at the provided vector of true DoFs of trial variables.
virtual void BuildMixedBilinearForms()
Build mixed bilinear forms (off-diagonal Jacobian contributions)
mfem::Array2D< const mfem::HypreParMatrix * > _h_blocks
void ApplyBoundaryLFIntegrators(const std::string &test_var_name, std::shared_ptr< mfem::ParLinearForm > form, NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMIntegratedBC > > > > &integrated_bc_map)
Apply boundary LinearFormIntegrators from integrated boundary conditions to the linear form associate...
std::vector< std::string > _test_var_names
Names of all test variables corresponding to linear forms in this equation system.
void FormJacobianMatrix(const mfem::Vector &u)
Compute Jacobian matrix at the provided vector of true DoFs of trial variables.
virtual void FormSystemOperator(mfem::OperatorHandle &op, mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
Form matrix-free representation of linear components of system operator.
mfem::Array< int > & GetEssentialBoundaryMarkers(const std::string &var_name)
Return the essential boundary attribute marker array for a given trial variable.
void ApplyBoundaryNLFIntegrators(const std::string &test_var_name, std::shared_ptr< mfem::ParNonlinearForm > form, NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMIntegratedBC > > > > &integrated_bc_map, std::optional< mfem::real_t > scale_factor=std::nullopt)
Apply boundary NonlinearFormIntegrators from integrated boundary conditions to the nonlinear form ass...
mfem::OperatorHandle _linear_operator
const mfem::Vector * _linearization_point
std::shared_ptr< mfem::ParNonlinearForm > BuildNonlinearFormForFESpace(const std::string &var_name, mfem::ParFiniteElementSpace &fespace, mfem::AssemblyLevel assembly_level)
Build a fresh ParNonlinearForm on the given FESpace using the same kernels as the main system's nonli...
NamedFieldsMap< mfem::ParLinearForm > _lfs
virtual void AddIntegratedBC(std::shared_ptr< MFEMIntegratedBC > kernel)
mfem::AssemblyLevel _assembly_level
virtual void AddKernel(std::shared_ptr< MFEMKernel > kernel)
Add kernels.
virtual void SetTrialVariableNames()
Set trial variable names from subset of coupled variables that have an associated test variable.
mfem::Array2D< const mfem::HypreParMatrix * > _jacobian_blocks
virtual void FormSystemMatrix(mfem::OperatorHandle &op, mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
Form matrix representation of linear components of system operator as a HypreParMatrix.
std::vector< mfem::Array< int > > _ess_tdof_lists
CoefficientManager * _coefficient_manager
virtual void SetTrialVariablesFromTrueVectors(const mfem::BlockVector &trueX) const
Update variable from solution vector after solve.
virtual void AddEssentialBC(std::shared_ptr< MFEMEssentialBC > bc)
Add BC associated with essentially constrained DoFs on boundaries.
NamedFieldsMap< mfem::ParBilinearForm > _blfs
void DeleteHBlocks()
Deletes the HypreParMatrix associated with any pointer stored in _h_blocks, and then proceeds to dele...
virtual void ComputeNonlinearResidual(const mfem::Vector &u, mfem::Vector &residual) const
Compute the contribution to the residual from nonlinear forms only.
mfem::OperatorHandle _jacobian
virtual void FormLinearSystem(mfem::OperatorHandle &op, mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
Form linear components of system based on on- and off-diagonal bilinear form contributions,...
NamedFieldsMap< std::vector< std::shared_ptr< MFEMEssentialBC > > > _essential_bc_map
Arrays to store essential BCs to act on each component of weak form.
virtual void Init(GridFunctions &gridfunctions, ComplexGridFunctions &cmplx_gridfunctions, mfem::AssemblyLevel assembly_level)
Initialise.
std::vector< std::string > _trial_var_names
Subset of _coupled_var_names of all variables corresponding to gridfunctions with degrees of freedom ...
NamedFieldsMap< NamedFieldsMap< mfem::ParMixedBilinearForm > > _mblfs
std::vector< std::unique_ptr< mfem::ParGridFunction > > _var_ess_constraints
Gridfunctions holding essential constraints from Dirichlet BCs.
void DeleteJacobianBlocks()
Deletes the HypreParMatrix associated with any pointer stored in _jacobian_blocks,...
void ApplyDomainLFIntegrators(const std::string &test_var_name, std::shared_ptr< mfem::ParLinearForm > form, NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMKernel > > > > &kernels_map)
Apply domain LinearFormIntegrators from kernels to the linear form associated with the supplied test ...
virtual void AddEliminatedVariableNameIfMissing(const std::string &eliminated_var_name)
Add eliminated variable to EquationSystem.
void Mult(const mfem::Vector &u, mfem::Vector &residual) const override
Compute residual y = Mu.
NamedFieldsMap< mfem::ParNonlinearForm > _nlfs
Moose::MFEM::GridFunctions _eliminated_variables
Pointers to coupled variables not part of the reduced EquationSystem.
const mfem::Vector & GetLinearizationPoint() const
The true-DoF vector used for the most recent Jacobian linearization.
std::vector< mfem::Array< int > > _ess_markers
std::vector< std::string > _eliminated_var_names
Names of all coupled variables without a corresponding test variable.
Moose::MFEM::GridFunctions * _gfuncs
virtual void BuildLinearForms()
Build linear forms and eliminate constrained DoFs.
virtual void AddCoupledVariableNameIfMissing(const std::string &coupled_var_name)
Add coupled variable to EquationSystem.
NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMIntegratedBC > > > > _integrated_bc_map
Arrays to store integrated BCs to act on each component of weak form.
NonlinearFormIntegrator which scales its results by a constant value.
Lightweight adaptor over an std::map from strings to pointer to T.
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.
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).