https://mooseframework.inl.gov
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"
13 #include "MFEMLinearSolverBase.h"
14 #include "CoefficientManager.h"
15 #include "libmesh/int_range.h"
16 
17 namespace Moose::MFEM
18 {
19 
21 {
22  DeleteHBlocks();
24 }
25 
26 void
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 
39 void
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 
49 bool
50 EquationSystem::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 
56 void
57 EquationSystem::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 
63 void
64 EquationSystem::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 
70 void
71 EquationSystem::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 
77 void
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 
92 void
93 EquationSystem::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 
115 void
116 EquationSystem::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 
138 void
139 EquationSystem::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 
152 void
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 
203 void
204 EquationSystem::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 
221 void
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 
245 void
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 
258 void
259 EquationSystem::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 
272 void
273 EquationSystem::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),
286  *_var_ess_constraints.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 
302 void
303 EquationSystem::FormSystemMatrix(mfem::OperatorHandle & op,
304  mfem::BlockVector & trueX,
305  mfem::BlockVector & trueRHS)
306 {
307  // Allocate block operator
308  DeleteHBlocks();
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),
332  *_var_ess_constraints.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),
345  *_var_ess_constraints.at(j),
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 
366 void
367 EquationSystem::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 
381 void
382 EquationSystem::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 
399 void
400 EquationSystem::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 
418 void
419 EquationSystem::FormJacobianMatrix(const mfem::Vector & u)
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 
449 mfem::Operator &
450 EquationSystem::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 
467 void
468 EquationSystem::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 
481 void
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 
508 void
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 
524 void
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 
545 void
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 
584 void
586 {
591 }
592 
593 void
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 
616 void
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 
648 void
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->isBoundaryRestricted()
666  ? form->AddBoundaryIntegrator(std::move(integ), bc->getBoundaryMarkers())
667  : form->AddBoundaryIntegrator(std::move(integ));
668  }
669  }
670  }
671 }
672 
673 void
675  const std::string & test_var_name,
676  std::shared_ptr<mfem::ParNonlinearForm> form,
677  NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMIntegratedBC>>>> &
678  integrated_bc_map,
679  std::optional<mfem::real_t> scale_factor)
680 {
681  if (integrated_bc_map.Has(test_var_name))
682  for (const auto & [trial_var_name, bcs] : integrated_bc_map.GetRef(test_var_name))
683  for (auto & bc : *bcs)
684  if (auto * integ = bc->createNLIntegrator())
685  {
686  if (_gradient_required && (test_var_name != trial_var_name))
687  mooseError(
688  "Support for Off-diagonal MFEM nonlinear boundary integrators in conjunction with "
689  "a nonlinear solver that requires a gradient is not currently "
690  "implemented. Boundary condition '",
691  bc->name(),
692  "' contributes to test variable '",
693  test_var_name,
694  "' from trial variable '",
695  trial_var_name,
696  "'.");
697 
698  _non_linear = true;
699  if (scale_factor.has_value())
700  integ = new NLScaleIntegrator(integ, scale_factor.value(), true);
701  bc->isBoundaryRestricted()
702  ? form->AddBoundaryIntegrator(std::move(integ), bc->getBoundaryMarkers())
703  : form->AddBoundaryIntegrator(std::move(integ));
704  }
705 }
706 
707 const mfem::Vector &
709 {
711  mooseError("EquationSystem::GetLinearizationPoint() called before GetGradient().");
712  return *_linearization_point;
713 }
714 
715 std::shared_ptr<mfem::ParBilinearForm>
716 EquationSystem::BuildBilinearFormForFESpace(const std::string & var_name,
717  mfem::ParFiniteElementSpace & fespace,
718  mfem::AssemblyLevel assembly_level)
719 {
720  auto blf = std::make_shared<mfem::ParBilinearForm>(&fespace);
721  blf->SetAssemblyLevel(assembly_level);
722  ApplyBoundaryBLFIntegrators<mfem::ParBilinearForm>(var_name, var_name, blf, _integrated_bc_map);
723  ApplyDomainBLFIntegrators<mfem::ParBilinearForm>(var_name, var_name, blf, _kernels_map);
724  blf->Assemble();
725  return blf;
726 }
727 
728 std::shared_ptr<mfem::ParNonlinearForm>
729 EquationSystem::BuildNonlinearFormForFESpace(const std::string & var_name,
730  mfem::ParFiniteElementSpace & fespace,
731  mfem::AssemblyLevel /*assembly_level*/)
732 {
733  auto nlf = std::make_shared<mfem::ParNonlinearForm>(&fespace);
734  ApplyDomainNLFIntegrators(var_name, nlf, _kernels_map, std::nullopt);
735  ApplyBoundaryNLFIntegrators(var_name, nlf, _integrated_bc_map, std::nullopt);
736  return nlf;
737 }
738 
739 mfem::Array<int> &
740 EquationSystem::GetEssentialBoundaryMarkers(const std::string & var_name)
741 {
742  for (const auto i : index_range(_trial_var_names))
743  if (_trial_var_names.at(i) == var_name)
744  return _ess_markers.at(i);
745 
746  mooseError("No essential boundary markers found for variable '", var_name, "'.");
747 }
748 
749 } // namespace Moose::MFEM
750 
751 #endif
std::string name(const ElemQuality q)
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...
NamedFieldsMap< mfem::ParBilinearForm > _blfs
virtual void EliminateCoupledVariables()
Perform trivial eliminations of coupled variables lacking corresponding test variables.
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:40
NamedFieldsMap< NamedFieldsMap< mfem::ParMixedBilinearForm > > _mblfs
virtual void AddTestVariableNameIfMissing(const std::string &test_var_name)
Add test variable to EquationSystem.
virtual void AddKernel(std::shared_ptr< MFEMKernel > kernel)
Add kernels.
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&#39;s nonli...
virtual void BuildBilinearForms()
Build bilinear forms (diagonal Jacobian contributions)
bool Has(const std::string &field_name) const
Predicate to check if a field is registered with name field_name.
std::vector< mfem::ParFiniteElementSpace * > _coupled_pfespaces
Pointers to finite element spaces associated with coupled variables.
const mfem::Vector & GetLinearizationPoint() const
The true-DoF vector used for the most recent Jacobian linearization.
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
virtual void SetTrialVariablesFromTrueVectors(const mfem::BlockVector &trueX) const
Update variable from solution vector after solve.
void DeleteJacobianBlocks()
Deletes the HypreParMatrix associated with any pointer stored in _jacobian_blocks, and then proceeds to delete all dynamically allocated memory for _jacobian_blocks itself, resetting all dimensions to zero.
bool VectorContainsName(const std::vector< std::string > &the_vector, const std::string &name) const
NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMIntegratedBC > > > > _integrated_bc_map
Arrays to store integrated BCs to act on each component of weak form.
std::vector< std::string > _eliminated_var_names
Names of all coupled variables without a corresponding test variable.
void FormJacobianMatrix(const mfem::Vector &u)
Compute Jacobian matrix at the provided vector of true DoFs of trial variables.
NamedFieldsMap< mfem::ParNonlinearForm > _nlfs
Owns the weak-form mathematics of a MOOSE MFEM problem.
Moose::MFEM::GridFunctions _eliminated_variables
Pointers to coupled variables not part of the reduced EquationSystem.
virtual void AddEssentialBC(std::shared_ptr< MFEMEssentialBC > bc)
Add BC associated with essentially constrained DoFs on boundaries.
NonlinearFormIntegrator which scales its results by a constant value.
std::vector< mfem::Array< int > > _ess_tdof_lists
CoefficientManager * _coefficient_manager
Lightweight adaptor over an std::map from strings to pointer to T.
mfem::AssemblyLevel _assembly_level
mfem::Array< int > & GetEssentialBoundaryMarkers(const std::string &var_name)
Return the essential boundary attribute marker array for a given trial variable.
std::vector< mfem::ParFiniteElementSpace * > _test_pfespaces
Pointers to finite element spaces associated with test variables.
void FormSystem(mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
Build all weak-form components via BuildEquationSystem(), form the constrained linear part of the sys...
void Mult(const mfem::Vector &u, mfem::Vector &residual) const override
Compute residual y = Mu.
mfem::Array< int > _block_true_offsets
std::vector< std::string > _trial_var_names
Subset of _coupled_var_names of all variables corresponding to gridfunctions with degrees of freedom ...
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...
NamedFieldsMap< std::vector< std::shared_ptr< MFEMEssentialBC > > > _essential_bc_map
Arrays to store essential BCs to act on each component of weak form.
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...
mfem::OperatorHandle _jacobian
NamedFieldsMap< NamedFieldsMap< std::vector< std::shared_ptr< MFEMKernel > > > > _kernels_map
Arrays to store kernels to act on each component of weak form.
std::vector< std::string > _test_var_names
Names of all test variables corresponding to linear forms in this equation system.
virtual void BuildEquationSystem()
Build all forms comprising this EquationSystem.
std::vector< std::unique_ptr< mfem::ParGridFunction > > _var_ess_constraints
Gridfunctions holding essential constraints from Dirichlet BCs.
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&#39;s biline...
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...
virtual void BuildMixedBilinearForms()
Build mixed bilinear forms (off-diagonal Jacobian contributions)
std::vector< mfem::Array< int > > _ess_markers
virtual void FormSystemOperator(mfem::OperatorHandle &op, mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
Form matrix-free representation of linear components of system operator.
mfem::Array2D< const mfem::HypreParMatrix * > _h_blocks
virtual void SetTrialVariableNames()
Set trial variable names from subset of coupled variables that have an associated test variable...
virtual void FormSystemMatrix(mfem::OperatorHandle &op, mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
Form matrix representation of linear components of system operator as a HypreParMatrix.
virtual void AddCoupledVariableNameIfMissing(const std::string &coupled_var_name)
Add coupled variable to EquationSystem.
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, populate solution and RHS vectors of true DoFs, and apply constraints.
void DeleteHBlocks()
Deletes the HypreParMatrix associated with any pointer stored in _h_blocks, and then proceeds to dele...
std::vector< std::string > _coupled_var_names
Names of all trial variables of kernels and boundary conditions added to this EquationSystem.
void Register(const std::string &field_name, FieldArgs &&... args)
Construct new field with name field_name and register.
void markSolutionChanged()
Notify quadrature function coefficients that solution variables have changed, marking the stored valu...
virtual void BuildNonlinearForms()
Build non-linear action forms.
NamedFieldsMap< mfem::ParLinearForm > _lfs
virtual void BuildLinearForms()
Build linear forms and eliminate constrained DoFs.
IntRange< T > make_range(T beg, T end)
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...
virtual void ApplyEssentialBCs()
Update all essentially constrained true DoF markers and values on boundaries.
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 ...
Utilities for converting between vector(s) of libMesh Points and MFEM Vector(s).
mfem::OperatorHandle _linear_operator
virtual void AddEliminatedVariableNameIfMissing(const std::string &eliminated_var_name)
Add eliminated variable to EquationSystem.
virtual void Init(GridFunctions &gridfunctions, ComplexGridFunctions &cmplx_gridfunctions, mfem::AssemblyLevel assembly_level)
Initialise.
const mfem::Vector * _linearization_point
T & GetRef(const std::string &field_name) const
Returns a reference to a field.
mfem::Operator & GetGradient(const mfem::Vector &u) const override
Get Jacobian at the provided vector of true DoFs of trial variables.
virtual void ComputeNonlinearResidual(const mfem::Vector &u, mfem::Vector &residual) const
Compute the contribution to the residual from nonlinear forms only.
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 AddIntegratedBC(std::shared_ptr< MFEMIntegratedBC > kernel)
auto index_range(const T &sizable)
mfem::Array2D< const mfem::HypreParMatrix * > _jacobian_blocks
Moose::MFEM::GridFunctions * _gfuncs