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 _ess_markers.at(i).SetSize(trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max(), 0);
238 // Set strongly constrained DoFs of trial_gf on essential boundaries and add markers for all
239 // essential boundaries to the _ess_markers array
240 ApplyEssentialBC(trial_var_name, trial_gf, _ess_markers.at(i));
241 trial_gf.ParFESpace()->GetEssentialTrueDofs(_ess_markers.at(i), _ess_tdof_lists.at(i));
242 }
243}
244
245void
247{
248 for (const auto & test_var_name : _test_var_names)
249 for (const auto & eliminated_var_name : _eliminated_var_names)
250 if (_mblfs.Has(test_var_name) && _mblfs.Get(test_var_name)->Has(eliminated_var_name) &&
251 !VectorContainsName(_test_var_names, eliminated_var_name))
252 {
253 auto & mblf = *_mblfs.Get(test_var_name)->Get(eliminated_var_name);
254 mblf.AddMult(*_eliminated_variables.Get(eliminated_var_name), *_lfs.Get(test_var_name), -1);
255 }
256}
257
258void
259EquationSystem::FormLinearSystem(mfem::OperatorHandle & op,
260 mfem::BlockVector & trueX,
261 mfem::BlockVector & trueRHS)
262{
263 mooseAssert(_test_var_names.size() == _trial_var_names.size(),
264 "Number of test and trial variables must be the same for block matrix assembly.");
265
266 if (_assembly_level == mfem::AssemblyLevel::LEGACY)
267 FormSystemMatrix(op, trueX, trueRHS);
268 else
269 FormSystemOperator(op, trueX, trueRHS);
270}
271
272void
273EquationSystem::FormSystemOperator(mfem::OperatorHandle & op,
274 mfem::BlockVector & trueX,
275 mfem::BlockVector & trueRHS)
276{
277 mooseAssert(_test_var_names.size() == 1 && _test_var_names.size() == _trial_var_names.size(),
278 "Non-legacy assembly is only supported for single test and trial variable systems");
279
280 auto & test_var_name = _test_var_names.at(0);
281 mfem::Vector aux_x, aux_rhs;
282 mfem::OperatorPtr aux_a;
283
284 auto blf = _blfs.Get(test_var_name);
285 blf->FormLinearSystem(_ess_tdof_lists.at(0),
287 *_lfs.Get(test_var_name),
288 aux_a,
289 aux_x,
290 aux_rhs,
291 /*copy_interior=*/true);
292
293 trueX.GetBlock(0) = aux_x;
294 trueRHS.GetBlock(0) = aux_rhs;
295 trueX.SyncFromBlocks();
296 trueRHS.SyncFromBlocks();
297
298 op.Reset(aux_a.Ptr());
299 aux_a.SetOperatorOwner(false);
300}
301
302void
303EquationSystem::FormSystemMatrix(mfem::OperatorHandle & op,
304 mfem::BlockVector & trueX,
305 mfem::BlockVector & trueRHS)
306{
307 // Allocate block operator
309 _h_blocks.SetSize(_test_var_names.size(), _trial_var_names.size());
310 _h_blocks = nullptr;
311 // Zero out RHS and sync memory
312 trueRHS = 0.0;
313 trueRHS.SyncToBlocks();
314
315 for (const auto i : index_range(_test_var_names))
316 {
317 auto test_var_name = _test_var_names.at(i);
318
319 for (const auto j : index_range(_trial_var_names))
320 {
321 auto trial_var_name = _trial_var_names.at(j);
322
323 mfem::Vector aux_x, aux_rhs;
324 mfem::ParLinearForm aux_lf(_test_pfespaces.at(i));
325 mfem::HypreParMatrix * aux_a = new mfem::HypreParMatrix;
326
327 if (test_var_name == trial_var_name)
328 {
329 mooseAssert(i == j, "Trial and test variables must have the same ordering.");
330 auto blf = _blfs.Get(test_var_name);
331 blf->FormLinearSystem(_ess_tdof_lists.at(j),
333 *_lfs.Get(test_var_name),
334 *aux_a,
335 aux_x,
336 aux_rhs,
337 /*copy_interior=*/true);
338 trueX.GetBlock(j) = aux_x;
339 }
340 else if (_mblfs.Has(test_var_name) && _mblfs.Get(test_var_name)->Has(trial_var_name))
341 {
342 auto mblf = _mblfs.Get(test_var_name)->Get(trial_var_name);
343 mblf->FormRectangularLinearSystem(_ess_tdof_lists.at(j),
344 _ess_tdof_lists.at(i),
346 aux_lf = 0,
347 *aux_a,
348 aux_x,
349 aux_rhs);
350 }
351 else
352 continue;
353
354 trueRHS.GetBlock(i) += aux_rhs;
355 _h_blocks(i, j) = aux_a;
356 }
357 }
358 // Sync memory
359 trueX.SyncFromBlocks();
360 trueRHS.SyncFromBlocks();
361
362 // Create monolithic matrix
363 op.Reset(mfem::HypreParMatrixFromBlocks(_h_blocks));
364}
365
366void
367EquationSystem::FormSystem(mfem::BlockVector & trueX, mfem::BlockVector & trueRHS)
368{
370 height = trueX.Size();
371 width = trueRHS.Size();
372 // Store block offsets
373 _block_true_offsets.SetSize(trueX.NumBlocks() + 1);
374 _block_true_offsets[0] = 0;
375 for (unsigned i = 0; i < _trial_var_names.size(); i++)
376 _block_true_offsets[i + 1] = trueX.BlockSize(i);
377 _block_true_offsets.PartialSum();
378 FormLinearSystem(_linear_operator, trueX, trueRHS);
379}
380
381void
382EquationSystem::Mult(const mfem::Vector & sol, mfem::Vector & residual) const
383{
384 if (_non_linear)
385 {
386 ComputeNonlinearResidual(sol, residual);
387 _linear_operator->AddMult(sol, residual);
388 }
389 else
390 {
391 residual = 0.0;
392 _linear_operator->Mult(sol, residual);
393 }
394
395 sol.HostRead();
396 residual.HostRead();
397}
398
399void
400EquationSystem::ComputeNonlinearResidual(const mfem::Vector & sol, mfem::Vector & residual) const
401{
402 mooseAssert(_non_linear, "Should not be calling this method if our forms are not nonlinear");
403 residual = 0.0;
404
405 const mfem::BlockVector block_solution(const_cast<mfem::Vector &>(sol), _block_true_offsets);
406 SetTrialVariablesFromTrueVectors(block_solution);
407
408 mfem::BlockVector block_residual(residual, _block_true_offsets);
409 for (unsigned int i = 0; i < _test_var_names.size(); i++)
410 {
411 auto & test_var_name = _test_var_names.at(i);
412 auto nlf = _nlfs.GetShared(test_var_name);
413 nlf->Mult(block_solution.GetBlock(i), block_residual.GetBlock(i));
414 block_residual.GetBlock(i).SyncAliasMemory(block_residual);
415 }
416}
417
418void
420{
422 _jacobian_blocks.SetSize(_test_var_names.size(), _trial_var_names.size());
423 _jacobian_blocks = nullptr;
424
425 const mfem::BlockVector update_vector(const_cast<mfem::Vector &>(u), _block_true_offsets);
426 for (const auto i : index_range(_test_var_names))
427 {
428 auto test_var_name = _test_var_names.at(i);
429 if (_nlfs.Has(test_var_name))
430 {
431 auto nlf = _nlfs.Get(test_var_name);
432 mfem::HypreParMatrix * nlf_jac =
433 dynamic_cast<mfem::HypreParMatrix *>(&nlf->GetGradient(update_vector.GetBlock(i)));
434 mooseAssert(nlf_jac,
435 "Jacobian contribution of nonlinear form associated with " + test_var_name +
436 " is not castable into a HypreParMatrix");
437 _jacobian_blocks(i, i) = mfem::ParAdd(_h_blocks(i, i), nlf_jac);
438 }
439 else
440 _jacobian_blocks(i, i) = _h_blocks(i, i);
441 for (const auto j : index_range(_trial_var_names))
442 if (i != j) // nlf->GetGradient only contributes to on-diagonal blocks
443 _jacobian_blocks(i, j) = _h_blocks(i, j);
444 }
445 // Create monolithic matrix
446 _jacobian.Reset(mfem::HypreParMatrixFromBlocks(_jacobian_blocks));
447}
448
449mfem::Operator &
450EquationSystem::GetGradient(const mfem::Vector & u) const
451{
453
454 if (_non_linear)
455 {
456 if (_assembly_level != mfem::AssemblyLevel::LEGACY)
457 mooseError("MFEM nonlinear solvers that require GetGradient() currently require legacy "
458 "assembly in EquationSystem.");
459 const_cast<EquationSystem *>(this)->FormJacobianMatrix(u);
460 }
461 else
463
464 return *_jacobian;
465}
466
467void
468EquationSystem::SetTrialVariablesFromTrueVectors(const mfem::BlockVector & trueX) const
469{
470 for (const auto i : index_range(_trial_var_names))
471 {
472 auto & trial_var_name = _trial_var_names.at(i);
473 trueX.GetBlock(i).SyncMemory(trueX);
474 _gfuncs->Get(trial_var_name)->Distribute(&(trueX.GetBlock(i)));
475 }
476 // Solution variables changed: stored projections of solution-dependent coefficients are stale.
479}
480
481void
483{
484 // Register linear forms
485 for (const auto i : index_range(_test_var_names))
486 {
487 auto test_var_name = _test_var_names.at(i);
488 _lfs.Register(test_var_name, std::make_shared<mfem::ParLinearForm>(_test_pfespaces.at(i)));
489 _lfs.GetRef(test_var_name) = 0.0;
490 }
491
492 for (auto & test_var_name : _test_var_names)
493 {
494 // Apply kernels
495 auto lf = _lfs.GetShared(test_var_name);
496 ApplyDomainLFIntegrators(test_var_name, lf, _kernels_map);
498 lf->Assemble();
499 }
500
501 // Apply essential boundary conditions
503
504 // Eliminate trivially eliminated variables by subtracting contributions from linear forms
506}
507
508void
510{
511 // Register non-linear Action forms
512 for (const auto i : index_range(_test_var_names))
513 {
514 auto test_var_name = _test_var_names.at(i);
515 _nlfs.Register(test_var_name, std::make_shared<mfem::ParNonlinearForm>(_test_pfespaces.at(i)));
516 // Apply kernels
517 auto nlf = _nlfs.GetShared(test_var_name);
518 nlf->SetEssentialTrueDofs(_ess_tdof_lists.at(i));
519 ApplyDomainNLFIntegrators(test_var_name, nlf, _kernels_map, std::nullopt);
520 ApplyBoundaryNLFIntegrators(test_var_name, nlf, _integrated_bc_map, std::nullopt);
521 }
522}
523
524void
526{
527 // Register bilinear forms
528 for (const auto i : index_range(_test_var_names))
529 {
530 auto test_var_name = _test_var_names.at(i);
531 _blfs.Register(test_var_name, std::make_shared<mfem::ParBilinearForm>(_test_pfespaces.at(i)));
532
533 // Apply kernels
534 auto blf = _blfs.GetShared(test_var_name);
535 blf->SetAssemblyLevel(_assembly_level);
536 ApplyBoundaryBLFIntegrators<mfem::ParBilinearForm>(
537 test_var_name, test_var_name, blf, _integrated_bc_map);
538 ApplyDomainBLFIntegrators<mfem::ParBilinearForm>(
539 test_var_name, test_var_name, blf, _kernels_map);
540 // Assemble
541 blf->Assemble();
542 }
543}
544
545void
547{
548 // Register mixed bilinear forms. Note that not all combinations may
549 // have a kernel.
550
551 // Create mblf for each test/coupled variable pair with an added kernel.
552 // Mixed bilinear forms with coupled variables that are not trial variables are
553 // associated with contributions from eliminated variables.
554 for (const auto i : index_range(_test_var_names))
555 {
556 auto test_var_name = _test_var_names.at(i);
557 auto test_mblfs = std::make_shared<Moose::MFEM::NamedFieldsMap<mfem::ParMixedBilinearForm>>();
558 for (const auto j : index_range(_coupled_var_names))
559 {
560 const auto & coupled_var_name = _coupled_var_names.at(j);
561 auto mblf = std::make_shared<mfem::ParMixedBilinearForm>(_coupled_pfespaces.at(j),
562 _test_pfespaces.at(i));
563 // Register MixedBilinearForm if kernels exist for it, and assemble kernels
564 if (_kernels_map.Has(test_var_name) &&
565 _kernels_map.Get(test_var_name)->Has(coupled_var_name) &&
566 test_var_name != coupled_var_name)
567 {
568 mblf->SetAssemblyLevel(_assembly_level);
569 // Apply all mixed kernels with this test/trial pair
570 ApplyDomainBLFIntegrators<mfem::ParMixedBilinearForm>(
571 coupled_var_name, test_var_name, mblf, _kernels_map);
572 // Assemble mixed bilinear forms
573 mblf->Assemble();
574 // Register mixed bilinear forms associated with a single trial variable
575 // for the current test variable
576 test_mblfs->Register(coupled_var_name, mblf);
577 }
578 }
579 // Register all mixed bilinear form sets associated with a single test variable
580 _mblfs.Register(test_var_name, test_mblfs);
581 }
582}
583
584void
592
593void
595 const std::string & test_var_name,
596 std::shared_ptr<mfem::ParLinearForm> form,
597 NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>> & kernels_map)
598{
599 if (kernels_map.Has(test_var_name) && kernels_map.Get(test_var_name)->Has(test_var_name))
600 {
601 auto kernels = kernels_map.GetRef(test_var_name).GetRef(test_var_name);
602 for (auto & kernel : kernels)
603 {
604 mfem::LinearFormIntegrator * integ = kernel->createLFIntegrator();
605
606 if (integ)
607 {
608 kernel->isSubdomainRestricted()
609 ? form->AddDomainIntegrator(std::move(integ), kernel->getSubdomainMarkers())
610 : form->AddDomainIntegrator(std::move(integ));
611 }
612 }
613 }
614}
615
616void
618 const std::string & test_var_name,
619 std::shared_ptr<mfem::ParNonlinearForm> form,
620 NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>> & kernels_map,
621 std::optional<mfem::real_t> scale_factor)
622{
623 if (kernels_map.Has(test_var_name))
624 for (const auto & [trial_var_name, kernels] : kernels_map.GetRef(test_var_name))
625 for (auto & kernel : *kernels)
626 if (auto * integ = kernel->createNLIntegrator())
627 {
628 if (_gradient_required && (trial_var_name != test_var_name))
629 mooseError("Support for off-diagonal MFEM nonlinear domain integrators in conjunction "
630 "with a nonlinear solver that requires a gradient is not currently "
631 "implemented. Kernel '",
632 kernel->name(),
633 "' contributes to test variable '",
634 test_var_name,
635 "' from trial variable '",
636 trial_var_name,
637 "'.");
638
639 _non_linear = true;
640 if (scale_factor.has_value())
641 integ = new NLScaleIntegrator(integ, scale_factor.value(), true);
642 kernel->isSubdomainRestricted()
643 ? form->AddDomainIntegrator(std::move(integ), kernel->getSubdomainMarkers())
644 : form->AddDomainIntegrator(std::move(integ));
645 }
646}
647
648void
650 const std::string & test_var_name,
651 std::shared_ptr<mfem::ParLinearForm> form,
652 NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMIntegratedBC>>>> &
653 integrated_bc_map)
654{
655 if (integrated_bc_map.Has(test_var_name) &&
656 integrated_bc_map.Get(test_var_name)->Has(test_var_name))
657 {
658 auto bcs = integrated_bc_map.GetRef(test_var_name).GetRef(test_var_name);
659 for (auto & bc : bcs)
660 {
661 mfem::LinearFormIntegrator * integ = bc->createLFIntegrator();
662
663 if (integ)
664 {
665 bc->isDGBC() ? bc->isBoundaryRestricted()
666 ? form->AddBdrFaceIntegrator(std::move(integ), bc->getBoundaryMarkers())
667 : form->AddBdrFaceIntegrator(std::move(integ))
668 : bc->isBoundaryRestricted()
669 ? form->AddBoundaryIntegrator(std::move(integ), bc->getBoundaryMarkers())
670 : form->AddBoundaryIntegrator(std::move(integ));
671 }
672 }
673 }
674}
675
676void
678 const std::string & test_var_name,
679 std::shared_ptr<mfem::ParNonlinearForm> form,
680 NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMIntegratedBC>>>> &
681 integrated_bc_map,
682 std::optional<mfem::real_t> scale_factor)
683{
684 if (integrated_bc_map.Has(test_var_name))
685 for (const auto & [trial_var_name, bcs] : integrated_bc_map.GetRef(test_var_name))
686 for (auto & bc : *bcs)
687 if (auto * integ = bc->createNLIntegrator())
688 {
689 if (_gradient_required && (test_var_name != trial_var_name))
691 "Support for Off-diagonal MFEM nonlinear boundary integrators in conjunction with "
692 "a nonlinear solver that requires a gradient is not currently "
693 "implemented. Boundary condition '",
694 bc->name(),
695 "' contributes to test variable '",
696 test_var_name,
697 "' from trial variable '",
698 trial_var_name,
699 "'.");
700
701 _non_linear = true;
702 if (scale_factor.has_value())
703 integ = new NLScaleIntegrator(integ, scale_factor.value(), true);
704 bc->isBoundaryRestricted()
705 ? form->AddBoundaryIntegrator(std::move(integ), bc->getBoundaryMarkers())
706 : form->AddBoundaryIntegrator(std::move(integ));
707 }
708}
709
710const mfem::Vector &
712{
714 mooseError("EquationSystem::GetLinearizationPoint() called before GetGradient().");
715 return *_linearization_point;
716}
717
718std::shared_ptr<mfem::ParBilinearForm>
720 mfem::ParFiniteElementSpace & fespace,
721 mfem::AssemblyLevel assembly_level)
722{
723 auto blf = std::make_shared<mfem::ParBilinearForm>(&fespace);
724 blf->SetAssemblyLevel(assembly_level);
725 ApplyBoundaryBLFIntegrators<mfem::ParBilinearForm>(var_name, var_name, blf, _integrated_bc_map);
726 ApplyDomainBLFIntegrators<mfem::ParBilinearForm>(var_name, var_name, blf, _kernels_map);
727 blf->Assemble();
728 return blf;
729}
730
731std::shared_ptr<mfem::ParNonlinearForm>
733 mfem::ParFiniteElementSpace & fespace,
734 mfem::AssemblyLevel /*assembly_level*/)
735{
736 auto nlf = std::make_shared<mfem::ParNonlinearForm>(&fespace);
737 ApplyDomainNLFIntegrators(var_name, nlf, _kernels_map, std::nullopt);
738 ApplyBoundaryNLFIntegrators(var_name, nlf, _integrated_bc_map, std::nullopt);
739 return nlf;
740}
741
742mfem::Array<int> &
744{
745 for (const auto i : index_range(_trial_var_names))
746 if (_trial_var_names.at(i) == var_name)
747 return _ess_markers.at(i);
748
749 mooseError("No essential boundary markers found for variable '", var_name, "'.");
750}
751
752} // namespace Moose::MFEM
753
754#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).