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 "libmesh/int_range.h"
15 
16 namespace Moose::MFEM
17 {
18 
20 {
21  DeleteHBlocks();
23 }
24 
25 void
27 {
28  for (const auto i : make_range(_h_blocks.NumRows()))
29  for (const auto j : make_range(_h_blocks.NumCols()))
30  {
31  if (_jacobian_blocks.NumRows() && _jacobian_blocks(i, j) == _h_blocks(i, j))
32  _jacobian_blocks(i, j) = nullptr;
33  delete _h_blocks(i, j);
34  }
35  _h_blocks.DeleteAll();
36 }
37 
38 void
40 {
41  for (const auto i : make_range(_jacobian_blocks.NumRows()))
42  for (const auto j : make_range(_jacobian_blocks.NumCols()))
43  if (!_h_blocks.NumRows() || _jacobian_blocks(i, j) != _h_blocks(i, j))
44  delete _jacobian_blocks(i, j);
45  _jacobian_blocks.DeleteAll();
46 }
47 
48 bool
49 EquationSystem::VectorContainsName(const std::vector<std::string> & the_vector,
50  const std::string & name) const
51 {
52  return std::find(the_vector.begin(), the_vector.end(), name) != the_vector.end();
53 }
54 
55 void
56 EquationSystem::AddCoupledVariableNameIfMissing(const std::string & coupled_var_name)
57 {
58  if (!VectorContainsName(_coupled_var_names, coupled_var_name))
59  _coupled_var_names.push_back(coupled_var_name);
60 }
61 
62 void
63 EquationSystem::AddEliminatedVariableNameIfMissing(const std::string & eliminated_var_name)
64 {
65  if (!VectorContainsName(_eliminated_var_names, eliminated_var_name))
66  _eliminated_var_names.push_back(eliminated_var_name);
67 }
68 
69 void
70 EquationSystem::AddTestVariableNameIfMissing(const std::string & test_var_name)
71 {
72  if (!VectorContainsName(_test_var_names, test_var_name))
73  _test_var_names.push_back(test_var_name);
74 }
75 
76 void
78 {
79  // If a coupled variable has an equation associated with it,
80  // add it to the set of trial variables.
81  for (const auto & test_var_name : _test_var_names)
82  if (VectorContainsName(_coupled_var_names, test_var_name))
83  _trial_var_names.push_back(test_var_name);
84 
85  // Otherwise, add it to the set of eliminated variables.
86  for (const auto & coupled_var_name : _coupled_var_names)
87  if (!VectorContainsName(_test_var_names, coupled_var_name))
88  _eliminated_var_names.push_back(coupled_var_name);
89 }
90 
91 void
92 EquationSystem::AddKernel(std::shared_ptr<MFEMKernel> kernel)
93 {
94  const auto & trial_var_name = kernel->getTrialVariableName();
95  const auto & test_var_name = kernel->getTestVariableName();
96  AddCoupledVariableNameIfMissing(trial_var_name);
97  AddTestVariableNameIfMissing(test_var_name);
98  // Register new kernels map if not present for the test variable
99  if (!_kernels_map.Has(test_var_name))
100  {
101  auto kernel_field_map =
102  std::make_shared<Moose::MFEM::NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>>();
103  _kernels_map.Register(test_var_name, std::move(kernel_field_map));
104  }
105  // Register new kernels map if not present for the test/trial variable pair
106  if (!_kernels_map.Get(test_var_name)->Has(trial_var_name))
107  {
108  auto kernels = std::make_shared<std::vector<std::shared_ptr<MFEMKernel>>>();
109  _kernels_map.Get(test_var_name)->Register(trial_var_name, std::move(kernels));
110  }
111  _kernels_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(kernel));
112 }
113 
114 void
115 EquationSystem::AddIntegratedBC(std::shared_ptr<MFEMIntegratedBC> bc)
116 {
117  const auto & trial_var_name = bc->getTrialVariableName();
118  const auto & test_var_name = bc->getTestVariableName();
119  AddCoupledVariableNameIfMissing(trial_var_name);
120  AddTestVariableNameIfMissing(test_var_name);
121  // Register new integrated bc map if not present for the test variable
122  if (!_integrated_bc_map.Has(test_var_name))
123  {
124  auto integrated_bc_field_map = std::make_shared<
126  _integrated_bc_map.Register(test_var_name, std::move(integrated_bc_field_map));
127  }
128  // Register new integrated bc map if not present for the test/trial variable pair
129  if (!_integrated_bc_map.Get(test_var_name)->Has(trial_var_name))
130  {
131  auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMIntegratedBC>>>();
132  _integrated_bc_map.Get(test_var_name)->Register(trial_var_name, std::move(bcs));
133  }
134  _integrated_bc_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(bc));
135 }
136 
137 void
138 EquationSystem::AddEssentialBC(std::shared_ptr<MFEMEssentialBC> bc)
139 {
140  const auto & test_var_name = bc->getTestVariableName();
141  AddTestVariableNameIfMissing(test_var_name);
142  // Register new essential bc map if not present for the test variable
143  if (!_essential_bc_map.Has(test_var_name))
144  {
145  auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMEssentialBC>>>();
146  _essential_bc_map.Register(test_var_name, std::move(bcs));
147  }
148  _essential_bc_map.GetRef(test_var_name).push_back(std::move(bc));
149 }
150 
151 void
153  Moose::MFEM::ComplexGridFunctions & cmplx_gridfunctions,
154  mfem::AssemblyLevel assembly_level)
155 {
156  _assembly_level = assembly_level;
157 
158  if (cmplx_gridfunctions.size())
159  mooseError("Complex variables have been created but the executioner numeric type has not been "
160  "set to complex. Please set Executioner/numeric_type = complex.");
161 
162  // Extract which coupled variables are to be trivially eliminated and which are trial variables
164 
165  for (auto & test_var_name : _test_var_names)
166  {
167  if (!gridfunctions.Has(test_var_name))
168  {
169  mooseError("MFEM variable ",
170  test_var_name,
171  " requested by equation system during initialization was "
172  "not found in gridfunctions");
173  }
174  // Store pointers to test FESpaces
175  _test_pfespaces.push_back(gridfunctions.Get(test_var_name)->ParFESpace());
176  }
177 
178  for (auto & trial_var_name : _trial_var_names)
179  {
180  if (!gridfunctions.Has(trial_var_name))
181  {
182  mooseError("MFEM variable ",
183  trial_var_name,
184  " requested by equation system during initialization was "
185  "not found in gridfunctions");
186  }
187  // Create auxiliary gridfunctions for storing essential constraints from Dirichlet conditions
188  _var_ess_constraints.emplace_back(
189  std::make_unique<mfem::ParGridFunction>(gridfunctions.Get(trial_var_name)->ParFESpace()));
190  }
191 
192  // Store pointers to FESpaces of all coupled variables
193  for (auto & coupled_var_name : _coupled_var_names)
194  _coupled_pfespaces.push_back(gridfunctions.Get(coupled_var_name)->ParFESpace());
195 
196  // Store pointers to coupled variable GridFunctions that are to be eliminated prior to forming the
197  // jacobian
198  for (auto & eliminated_var_name : _eliminated_var_names)
199  _eliminated_variables.Register(eliminated_var_name,
200  gridfunctions.GetShared(eliminated_var_name));
201 
202  // Get a reference to the GridFunctions
203  _gfuncs = &gridfunctions;
204 }
205 
206 void
207 EquationSystem::ApplyEssentialBC(const std::string & var_name,
208  mfem::ParGridFunction & trial_gf,
209  mfem::Array<int> & global_ess_markers)
210 {
211  if (_essential_bc_map.Has(var_name))
212  {
213  auto & bcs = _essential_bc_map.GetRef(var_name);
214  for (auto & bc : bcs)
215  {
216  // Set constrained DoFs values on essential boundaries
217  bc->ApplyBC(trial_gf);
218  // Fetch marker array labelling essential boundaries of current BC
219  mfem::Array<int> ess_bdrs(bc->getBoundaryMarkers());
220  // Add these boundary markers to the set of markers labelling all essential boundaries
221  for (const auto i : make_range(trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max()))
222  global_ess_markers[i] = std::max(global_ess_markers[i], ess_bdrs[i]);
223  }
224  }
225 }
226 
227 void
229 {
230  _ess_tdof_lists.resize(_trial_var_names.size());
231  for (const auto i : index_range(_trial_var_names))
232  {
233  const auto & trial_var_name = _trial_var_names.at(i);
234  mfem::ParGridFunction & trial_gf = *_var_ess_constraints.at(i);
235 
236  // Make sure we update the size, if this mesh has changed recently for instance
237  trial_gf.Update();
238 
239  // Initial guess for iterative solvers (initial condition or the previous time step solution)
240  trial_gf = _gfuncs->GetRef(trial_var_name);
241 
242  mfem::Array<int> global_ess_markers(trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max());
243  global_ess_markers = 0;
244  // Set strongly constrained DoFs of trial_gf on essential boundaries and add markers for all
245  // essential boundaries to the global_ess_markers array
246  ApplyEssentialBC(trial_var_name, trial_gf, global_ess_markers);
247  trial_gf.ParFESpace()->GetEssentialTrueDofs(global_ess_markers, _ess_tdof_lists.at(i));
248  }
249 }
250 
251 void
253 {
254  for (const auto & test_var_name : _test_var_names)
255  for (const auto & eliminated_var_name : _eliminated_var_names)
256  if (_mblfs.Has(test_var_name) && _mblfs.Get(test_var_name)->Has(eliminated_var_name) &&
257  !VectorContainsName(_test_var_names, eliminated_var_name))
258  {
259  auto & mblf = *_mblfs.Get(test_var_name)->Get(eliminated_var_name);
260  mblf.AddMult(*_eliminated_variables.Get(eliminated_var_name), *_lfs.Get(test_var_name), -1);
261  }
262 }
263 
264 void
265 EquationSystem::FormLinearSystem(mfem::OperatorHandle & op,
266  mfem::BlockVector & trueX,
267  mfem::BlockVector & trueRHS)
268 {
269  mooseAssert(_test_var_names.size() == _trial_var_names.size(),
270  "Number of test and trial variables must be the same for block matrix assembly.");
271 
272  if (_assembly_level == mfem::AssemblyLevel::LEGACY)
273  FormSystemMatrix(op, trueX, trueRHS);
274  else
275  {
276  mooseAssert(_test_var_names.size() == 1 && _test_var_names.size() == _trial_var_names.size(),
277  "Non-legacy assembly is only supported for single test and trial variable systems");
278  FormSystemOperator(op, trueX, trueRHS);
279  }
280 }
281 
282 void
283 EquationSystem::FormSystemOperator(mfem::OperatorHandle & op,
284  mfem::BlockVector & trueX,
285  mfem::BlockVector & trueRHS)
286 {
287  auto & test_var_name = _test_var_names.at(0);
288  mfem::Vector aux_x, aux_rhs;
289  mfem::OperatorPtr aux_a;
290 
291  auto blf = _blfs.Get(test_var_name);
292  blf->FormLinearSystem(_ess_tdof_lists.at(0),
293  *_var_ess_constraints.at(0),
294  *_lfs.Get(test_var_name),
295  aux_a,
296  aux_x,
297  aux_rhs,
298  /*copy_interior=*/true);
299 
300  trueX.GetBlock(0) = aux_x;
301  trueRHS.GetBlock(0) = aux_rhs;
302  trueX.SyncFromBlocks();
303  trueRHS.SyncFromBlocks();
304 
305  op.Reset(aux_a.Ptr());
306  aux_a.SetOperatorOwner(false);
307 }
308 
309 void
310 EquationSystem::FormSystemMatrix(mfem::OperatorHandle & op,
311  mfem::BlockVector & trueX,
312  mfem::BlockVector & trueRHS)
313 {
314  // Allocate block operator
315  DeleteHBlocks();
316  _h_blocks.SetSize(_test_var_names.size(), _trial_var_names.size());
317  _h_blocks = nullptr;
318  // Zero out RHS and sync memory
319  trueRHS = 0.0;
320  trueRHS.SyncToBlocks();
321 
322  for (const auto i : index_range(_test_var_names))
323  {
324  auto test_var_name = _test_var_names.at(i);
325 
326  for (const auto j : index_range(_trial_var_names))
327  {
328  auto trial_var_name = _trial_var_names.at(j);
329 
330  mfem::Vector aux_x, aux_rhs;
331  mfem::ParLinearForm aux_lf(_test_pfespaces.at(i));
332  mfem::HypreParMatrix * aux_a = new mfem::HypreParMatrix;
333 
334  if (test_var_name == trial_var_name)
335  {
336  mooseAssert(i == j, "Trial and test variables must have the same ordering.");
337  auto blf = _blfs.Get(test_var_name);
338  blf->FormLinearSystem(_ess_tdof_lists.at(j),
339  *_var_ess_constraints.at(j),
340  *_lfs.Get(test_var_name),
341  *aux_a,
342  aux_x,
343  aux_rhs,
344  /*copy_interior=*/true);
345  trueX.GetBlock(j) = aux_x;
346  }
347  else if (_mblfs.Has(test_var_name) && _mblfs.Get(test_var_name)->Has(trial_var_name))
348  {
349  auto mblf = _mblfs.Get(test_var_name)->Get(trial_var_name);
350  mblf->FormRectangularLinearSystem(_ess_tdof_lists.at(j),
351  _ess_tdof_lists.at(i),
352  *_var_ess_constraints.at(j),
353  aux_lf = 0,
354  *aux_a,
355  aux_x,
356  aux_rhs);
357  }
358  else
359  continue;
360 
361  trueRHS.GetBlock(i) += aux_rhs;
362  _h_blocks(i, j) = aux_a;
363  }
364  }
365  // Sync memory
366  trueX.SyncFromBlocks();
367  trueRHS.SyncFromBlocks();
368 
369  // Create monolithic matrix
370  op.Reset(mfem::HypreParMatrixFromBlocks(_h_blocks));
371 }
372 
373 void
374 EquationSystem::FormSystem(mfem::BlockVector & trueX, mfem::BlockVector & trueRHS)
375 {
376  height = trueX.Size();
377  width = trueRHS.Size();
378  // Store block offsets
379  _block_true_offsets.SetSize(trueX.NumBlocks() + 1);
380  _block_true_offsets[0] = 0;
381  for (unsigned i = 0; i < _trial_var_names.size(); i++)
382  _block_true_offsets[i + 1] = trueX.BlockSize(i);
383  _block_true_offsets.PartialSum();
384  FormLinearSystem(_linear_operator, trueX, trueRHS);
385 }
386 
387 void
388 EquationSystem::Mult(const mfem::Vector & sol, mfem::Vector & residual) const
389 {
390  if (_non_linear)
391  {
392  ComputeNonlinearResidual(sol, residual);
393  _linear_operator->AddMult(sol, residual);
394  }
395  else
396  {
397  residual = 0.0;
398  _linear_operator->Mult(sol, residual);
399  }
400 
401  sol.HostRead();
402  residual.HostRead();
403 }
404 
405 void
406 EquationSystem::ComputeNonlinearResidual(const mfem::Vector & sol, mfem::Vector & residual) const
407 {
408  mooseAssert(_non_linear, "Should not be calling this method if our forms are not nonlinear");
409  residual = 0.0;
410 
411  const mfem::BlockVector block_solution(const_cast<mfem::Vector &>(sol), _block_true_offsets);
412  SetTrialVariablesFromTrueVectors(block_solution);
413 
414  mfem::BlockVector block_residual(residual, _block_true_offsets);
415  for (unsigned int i = 0; i < _test_var_names.size(); i++)
416  {
417  auto & test_var_name = _test_var_names.at(i);
418  auto nlf = _nlfs.GetShared(test_var_name);
419  nlf->Mult(block_solution.GetBlock(i), block_residual.GetBlock(i));
420  block_residual.GetBlock(i).SyncAliasMemory(block_residual);
421  }
422 }
423 
424 void
425 EquationSystem::FormJacobianMatrix(const mfem::Vector & u)
426 {
428  _jacobian_blocks.SetSize(_test_var_names.size(), _trial_var_names.size());
429  _jacobian_blocks = nullptr;
430 
431  const mfem::BlockVector update_vector(const_cast<mfem::Vector &>(u), _block_true_offsets);
432  for (const auto i : index_range(_test_var_names))
433  {
434  auto test_var_name = _test_var_names.at(i);
435  if (_nlfs.Has(test_var_name))
436  {
437  auto nlf = _nlfs.Get(test_var_name);
438  mfem::HypreParMatrix * nlf_jac =
439  dynamic_cast<mfem::HypreParMatrix *>(&nlf->GetGradient(update_vector.GetBlock(i)));
440  mooseAssert(nlf_jac,
441  "Jacobian contribution of nonlinear form associated with " + test_var_name +
442  " is not castable into a HypreParMatrix");
443  _jacobian_blocks(i, i) = mfem::ParAdd(_h_blocks(i, i), nlf_jac);
444  }
445  else
446  _jacobian_blocks(i, i) = _h_blocks(i, i);
447  for (const auto j : index_range(_trial_var_names))
448  if (i != j) // nlf->GetGradient only contributes to on-diagonal blocks
449  _jacobian_blocks(i, j) = _h_blocks(i, j);
450  }
451  // Create monolithic matrix
452  _jacobian.Reset(mfem::HypreParMatrixFromBlocks(_jacobian_blocks));
453 }
454 
455 mfem::Operator &
456 EquationSystem::GetGradient(const mfem::Vector & u) const
457 {
458  if (_non_linear)
459  {
460  if (_assembly_level != mfem::AssemblyLevel::LEGACY)
461  mooseError("MFEM nonlinear solvers that require GetGradient() currently require legacy "
462  "assembly in EquationSystem.");
463  const_cast<EquationSystem *>(this)->FormJacobianMatrix(u);
464  }
465  else
467 
468  return *_jacobian;
469 }
470 
471 void
472 EquationSystem::SetTrialVariablesFromTrueVectors(const mfem::BlockVector & trueX) const
473 {
474  for (const auto i : index_range(_trial_var_names))
475  {
476  auto & trial_var_name = _trial_var_names.at(i);
477  trueX.GetBlock(i).SyncMemory(trueX);
478  _gfuncs->Get(trial_var_name)->Distribute(&(trueX.GetBlock(i)));
479  }
480 }
481 
482 void
484 {
485  // Register linear forms
486  for (const auto i : index_range(_test_var_names))
487  {
488  auto test_var_name = _test_var_names.at(i);
489  _lfs.Register(test_var_name, std::make_shared<mfem::ParLinearForm>(_test_pfespaces.at(i)));
490  _lfs.GetRef(test_var_name) = 0.0;
491  }
492 
493  for (auto & test_var_name : _test_var_names)
494  {
495  // Apply kernels
496  auto lf = _lfs.GetShared(test_var_name);
497  ApplyDomainLFIntegrators(test_var_name, lf, _kernels_map);
499  lf->Assemble();
500  }
501 
502  // Apply essential boundary conditions
504 
505  // Eliminate trivially eliminated variables by subtracting contributions from linear forms
507 }
508 
509 void
511 {
512  // Register non-linear Action forms
513  for (const auto i : index_range(_test_var_names))
514  {
515  auto test_var_name = _test_var_names.at(i);
516  _nlfs.Register(test_var_name, std::make_shared<mfem::ParNonlinearForm>(_test_pfespaces.at(i)));
517  // Apply kernels
518  auto nlf = _nlfs.GetShared(test_var_name);
519  nlf->SetEssentialTrueDofs(_ess_tdof_lists.at(i));
520  ApplyDomainNLFIntegrators(test_var_name, nlf, _kernels_map, std::nullopt);
521  ApplyBoundaryNLFIntegrators(test_var_name, nlf, _integrated_bc_map, std::nullopt);
522  }
523 }
524 
525 void
527 {
528  // Register bilinear forms
529  for (const auto i : index_range(_test_var_names))
530  {
531  auto test_var_name = _test_var_names.at(i);
532  _blfs.Register(test_var_name, std::make_shared<mfem::ParBilinearForm>(_test_pfespaces.at(i)));
533 
534  // Apply kernels
535  auto blf = _blfs.GetShared(test_var_name);
536  blf->SetAssemblyLevel(_assembly_level);
537  ApplyBoundaryBLFIntegrators<mfem::ParBilinearForm>(
538  test_var_name, test_var_name, blf, _integrated_bc_map);
539  ApplyDomainBLFIntegrators<mfem::ParBilinearForm>(
540  test_var_name, test_var_name, blf, _kernels_map);
541  // Assemble
542  blf->Assemble();
543  }
544 }
545 
546 void
548 {
549  // Register mixed bilinear forms. Note that not all combinations may
550  // have a kernel.
551 
552  // Create mblf for each test/coupled variable pair with an added kernel.
553  // Mixed bilinear forms with coupled variables that are not trial variables are
554  // associated with contributions from eliminated variables.
555  for (const auto i : index_range(_test_var_names))
556  {
557  auto test_var_name = _test_var_names.at(i);
558  auto test_mblfs = std::make_shared<Moose::MFEM::NamedFieldsMap<mfem::ParMixedBilinearForm>>();
559  for (const auto j : index_range(_coupled_var_names))
560  {
561  const auto & coupled_var_name = _coupled_var_names.at(j);
562  auto mblf = std::make_shared<mfem::ParMixedBilinearForm>(_coupled_pfespaces.at(j),
563  _test_pfespaces.at(i));
564  // Register MixedBilinearForm if kernels exist for it, and assemble kernels
565  if (_kernels_map.Has(test_var_name) &&
566  _kernels_map.Get(test_var_name)->Has(coupled_var_name) &&
567  test_var_name != coupled_var_name)
568  {
569  mblf->SetAssemblyLevel(_assembly_level);
570  // Apply all mixed kernels with this test/trial pair
571  ApplyDomainBLFIntegrators<mfem::ParMixedBilinearForm>(
572  coupled_var_name, test_var_name, mblf, _kernels_map);
573  // Assemble mixed bilinear forms
574  mblf->Assemble();
575  // Register mixed bilinear forms associated with a single trial variable
576  // for the current test variable
577  test_mblfs->Register(coupled_var_name, mblf);
578  }
579  }
580  // Register all mixed bilinear form sets associated with a single test variable
581  _mblfs.Register(test_var_name, test_mblfs);
582  }
583 }
584 
585 void
587 {
592 }
593 
594 void
596  const std::string & test_var_name,
597  std::shared_ptr<mfem::ParLinearForm> form,
598  NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>> & kernels_map)
599 {
600  if (kernels_map.Has(test_var_name) && kernels_map.Get(test_var_name)->Has(test_var_name))
601  {
602  auto kernels = kernels_map.GetRef(test_var_name).GetRef(test_var_name);
603  for (auto & kernel : kernels)
604  {
605  mfem::LinearFormIntegrator * integ = kernel->createLFIntegrator();
606 
607  if (integ)
608  {
609  kernel->isSubdomainRestricted()
610  ? form->AddDomainIntegrator(std::move(integ), kernel->getSubdomainMarkers())
611  : form->AddDomainIntegrator(std::move(integ));
612  }
613  }
614  }
615 }
616 
617 void
619  const std::string & test_var_name,
620  std::shared_ptr<mfem::ParNonlinearForm> form,
621  NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>> & kernels_map,
622  std::optional<mfem::real_t> scale_factor)
623 {
624  if (kernels_map.Has(test_var_name))
625  for (const auto & [trial_var_name, kernels] : kernels_map.GetRef(test_var_name))
626  for (auto & kernel : *kernels)
627  if (auto * integ = kernel->createNLIntegrator())
628  {
629  if (_solver_requires_gradient && (trial_var_name != test_var_name))
630  mooseError("Support for off-diagonal MFEM nonlinear domain integrators in conjunction "
631  "with a nonlinear solver that requires a gradient is not currently "
632  "implemented. Kernel '",
633  kernel->name(),
634  "' contributes to test variable '",
635  test_var_name,
636  "' from trial variable '",
637  trial_var_name,
638  "'.");
639 
640  _non_linear = true;
641  if (scale_factor.has_value())
642  integ = new NLScaleIntegrator(integ, scale_factor.value(), true);
643  kernel->isSubdomainRestricted()
644  ? form->AddDomainIntegrator(std::move(integ), kernel->getSubdomainMarkers())
645  : form->AddDomainIntegrator(std::move(integ));
646  }
647 }
648 
649 void
651  const std::string & test_var_name,
652  std::shared_ptr<mfem::ParLinearForm> form,
653  NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMIntegratedBC>>>> &
654  integrated_bc_map)
655 {
656  if (integrated_bc_map.Has(test_var_name) &&
657  integrated_bc_map.Get(test_var_name)->Has(test_var_name))
658  {
659  auto bcs = integrated_bc_map.GetRef(test_var_name).GetRef(test_var_name);
660  for (auto & bc : bcs)
661  {
662  mfem::LinearFormIntegrator * integ = bc->createLFIntegrator();
663 
664  if (integ)
665  {
666  bc->isBoundaryRestricted()
667  ? form->AddBoundaryIntegrator(std::move(integ), bc->getBoundaryMarkers())
668  : form->AddBoundaryIntegrator(std::move(integ));
669  }
670  }
671  }
672 }
673 
674 void
676  const std::string & test_var_name,
677  std::shared_ptr<mfem::ParNonlinearForm> form,
678  NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMIntegratedBC>>>> &
679  integrated_bc_map,
680  std::optional<mfem::real_t> scale_factor)
681 {
682  if (integrated_bc_map.Has(test_var_name))
683  for (const auto & [trial_var_name, bcs] : integrated_bc_map.GetRef(test_var_name))
684  for (auto & bc : *bcs)
685  if (auto * integ = bc->createNLIntegrator())
686  {
687  if (_solver_requires_gradient && (test_var_name != trial_var_name))
688  mooseError(
689  "Support for Off-diagonal MFEM nonlinear boundary integrators in conjunction with "
690  "a nonlinear solver that requires a gradient is not currently "
691  "implemented. Boundary condition '",
692  bc->name(),
693  "' contributes to test variable '",
694  test_var_name,
695  "' from trial variable '",
696  trial_var_name,
697  "'.");
698 
699  _non_linear = true;
700  if (scale_factor.has_value())
701  integ = new NLScaleIntegrator(integ, scale_factor.value(), true);
702  bc->isBoundaryRestricted()
703  ? form->AddBoundaryIntegrator(std::move(integ), bc->getBoundaryMarkers())
704  : form->AddBoundaryIntegrator(std::move(integ));
705  }
706 }
707 
708 void
710 {
711  if (solver.IsLOR())
712  {
713  if (Complex())
714  mooseError("LOR solve is not supported for complex equation systems.");
715  if (_test_var_names.size() > 1)
716  mooseError("LOR solve is only supported for single-variable systems");
717  solver.SetupLOR(*_blfs.Get(_test_var_names.at(0)), _ess_tdof_lists.at(0));
718  }
719 
720  mooseAssert(_linear_operator.Ptr(),
721  "If we are preparing a linear solver, we better have a linear operator");
723 }
724 
725 } // namespace Moose::MFEM
726 
727 #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.
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.
void PrepareLinearSolver(LinearSolverBase &solver)
Prepare the provided linear solver.
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
bool IsLOR() const
Returns whether or not this solver (or its preconditioner) uses LOR.
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.
virtual void SetOperator(mfem::OperatorHandle &op)
Updates the solver at the operator level.
void FormJacobianMatrix(const mfem::Vector &u)
Compute Jacobian matrix at the provided vector of true DoFs of trial variables.
NamedFieldsMap< mfem::ParNonlinearForm > _nlfs
Class to store weak form components (bilinear and linear forms, and optionally mixed and nonlinear fo...
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
Lightweight adaptor over an std::map from strings to pointer to T.
mfem::AssemblyLevel _assembly_level
auto max(const L &left, const R &right)
std::vector< mfem::ParFiniteElementSpace * > _test_pfespaces
Pointers to finite element spaces associated with test variables.
void FormSystem(mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
Assemble the linear part of the operator, assemble the right-hand side, apply essential and eliminate...
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 ...
Base class for linear MFEM solvers and preconditioners.
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
virtual void SetupLOR(mfem::ParBilinearForm &, mfem::Array< int > &)
Rebuild any Low-Order-Refined components from the unreduced bilinear form.
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.
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)
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.
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)
int size()
Returns the number of elements in the map.
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.
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)
virtual bool Complex() const
Whether this a complex equation system.
mfem::Array2D< const mfem::HypreParMatrix * > _jacobian_blocks
Moose::MFEM::GridFunctions * _gfuncs