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 "libmesh/int_range.h"
14 
15 namespace Moose::MFEM
16 {
17 
19 
20 void
22 {
23  for (const auto i : make_range(_h_blocks.NumRows()))
24  for (const auto j : make_range(_h_blocks.NumCols()))
25  delete _h_blocks(i, j);
26  _h_blocks.DeleteAll();
27 }
28 
29 bool
30 EquationSystem::VectorContainsName(const std::vector<std::string> & the_vector,
31  const std::string & name) const
32 {
33  auto iter = std::find(the_vector.begin(), the_vector.end(), name);
34  return (iter != the_vector.end());
35 }
36 
37 void
38 EquationSystem::AddCoupledVariableNameIfMissing(const std::string & coupled_var_name)
39 {
40  if (!VectorContainsName(_coupled_var_names, coupled_var_name))
41  _coupled_var_names.push_back(coupled_var_name);
42 }
43 
44 void
45 EquationSystem::AddTestVariableNameIfMissing(const std::string & test_var_name)
46 {
47  if (!VectorContainsName(_test_var_names, test_var_name))
48  _test_var_names.push_back(test_var_name);
49 }
50 
51 void
53 {
54  // If a coupled variable has an equation associated with it,
55  // add it to the set of trial variables.
56  for (const auto & coupled_var_name : _coupled_var_names)
57  {
58  if (VectorContainsName(_test_var_names, coupled_var_name))
59  _trial_var_names.push_back(coupled_var_name);
60  else
61  _eliminated_var_names.push_back(coupled_var_name);
62  }
63 }
64 
65 void
66 EquationSystem::AddKernel(std::shared_ptr<MFEMKernel> kernel)
67 {
68  AddTestVariableNameIfMissing(kernel->getTestVariableName());
69  AddCoupledVariableNameIfMissing(kernel->getTrialVariableName());
70  auto trial_var_name = kernel->getTrialVariableName();
71  auto test_var_name = kernel->getTestVariableName();
72  if (!_kernels_map.Has(test_var_name))
73  {
74  auto kernel_field_map =
75  std::make_shared<Moose::MFEM::NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>>();
76  _kernels_map.Register(test_var_name, std::move(kernel_field_map));
77  }
78  // Register new kernels map if not present for the test/trial variable
79  // pair
80  if (!_kernels_map.Get(test_var_name)->Has(trial_var_name))
81  {
82  auto kernels = std::make_shared<std::vector<std::shared_ptr<MFEMKernel>>>();
83  _kernels_map.Get(test_var_name)->Register(trial_var_name, std::move(kernels));
84  }
85  _kernels_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(kernel));
86 }
87 
88 void
89 EquationSystem::AddIntegratedBC(std::shared_ptr<MFEMIntegratedBC> bc)
90 {
91  AddTestVariableNameIfMissing(bc->getTestVariableName());
92  AddCoupledVariableNameIfMissing(bc->getTrialVariableName());
93  auto trial_var_name = bc->getTrialVariableName();
94  auto test_var_name = bc->getTestVariableName();
95  if (!_integrated_bc_map.Has(test_var_name))
96  {
97  auto integrated_bc_field_map = std::make_shared<
99  _integrated_bc_map.Register(test_var_name, std::move(integrated_bc_field_map));
100  }
101  // Register new integrated bc map if not present for the test/trial variable
102  // pair
103  if (!_integrated_bc_map.Get(test_var_name)->Has(trial_var_name))
104  {
105  auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMIntegratedBC>>>();
106  _integrated_bc_map.Get(test_var_name)->Register(trial_var_name, std::move(bcs));
107  }
108  _integrated_bc_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(bc));
109 }
110 
111 void
112 EquationSystem::AddEssentialBC(std::shared_ptr<MFEMEssentialBC> bc)
113 {
114  auto test_var_name = bc->getTestVariableName();
115  if (!_essential_bc_map.Has(test_var_name))
116  {
117  auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMEssentialBC>>>();
118  _essential_bc_map.Register(test_var_name, std::move(bcs));
119  }
120  _essential_bc_map.GetRef(test_var_name).push_back(std::move(bc));
121 }
122 
123 void
124 EquationSystem::Init(Moose::MFEM::GridFunctions & gridfunctions, mfem::AssemblyLevel assembly_level)
125 {
126  _assembly_level = assembly_level;
127 
128  for (auto & test_var_name : _test_var_names)
129  {
130  if (!gridfunctions.Has(test_var_name))
131  {
132  mooseError("MFEM variable ",
133  test_var_name,
134  " requested by equation system during initialisation was "
135  "not found in gridfunctions");
136  }
137  // Store pointers to test FESpaces
138  _test_pfespaces.push_back(gridfunctions.Get(test_var_name)->ParFESpace());
139  // Create auxiliary gridfunctions for storing essential constraints from Dirichlet conditions
140  _var_ess_constraints.emplace_back(
141  std::make_unique<mfem::ParGridFunction>(gridfunctions.Get(test_var_name)->ParFESpace()));
142  }
143 
144  // Extract which coupled variables are to be trivially eliminated and which are trial variables
146 
147  // Store pointers to FESpaces of all coupled variables
148  for (auto & coupled_var_name : _coupled_var_names)
149  _coupled_pfespaces.push_back(gridfunctions.Get(coupled_var_name)->ParFESpace());
150 
151  // Store pointers to coupled variable GridFunctions that are to be eliminated prior to forming the
152  // jacobian
153  for (auto & eliminated_var_name : _eliminated_var_names)
154  _eliminated_variables.Register(eliminated_var_name,
155  gridfunctions.GetShared(eliminated_var_name));
156 }
157 
158 void
159 EquationSystem::ApplyEssentialBC(const std::string & var_name,
160  mfem::ParGridFunction & trial_gf,
161  mfem::Array<int> & global_ess_markers)
162 {
163  if (_essential_bc_map.Has(var_name))
164  {
165  auto & bcs = _essential_bc_map.GetRef(var_name);
166  for (auto & bc : bcs)
167  {
168  // Set constrained DoFs values on essential boundaries
169  bc->ApplyBC(trial_gf);
170  // Fetch marker array labelling essential boundaries of current BC
171  mfem::Array<int> ess_bdrs(bc->getBoundaryMarkers());
172  // Add these boundary markers to the set of markers labelling all essential boundaries
173  for (auto it = 0; it != trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max(); ++it)
174  global_ess_markers[it] = std::max(global_ess_markers[it], ess_bdrs[it]);
175  }
176  }
177 }
178 
179 void
181 {
182  _ess_tdof_lists.resize(_test_var_names.size());
183  for (const auto i : index_range(_test_var_names))
184  {
185  const auto & test_var_name = _test_var_names.at(i);
186  mfem::ParGridFunction & trial_gf = *(_var_ess_constraints.at(i));
187  mfem::Array<int> global_ess_markers(trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max());
188  global_ess_markers = 0;
189  // Set strongly constrained DoFs of trial_gf on essential boundaries and add markers for all
190  // essential boundaries to the global_ess_markers array
191  ApplyEssentialBC(test_var_name, trial_gf, global_ess_markers);
192  trial_gf.ParFESpace()->GetEssentialTrueDofs(global_ess_markers, _ess_tdof_lists.at(i));
193  }
194 }
195 
196 void
198 {
199  for (const auto & test_var_name : _test_var_names)
200  {
201  auto lf = _lfs.Get(test_var_name);
202  for (const auto & eliminated_var_name : _eliminated_var_names)
203  {
204  if (_mblfs.Has(test_var_name) && _mblfs.Get(test_var_name)->Has(eliminated_var_name))
205  {
206  auto mblf = _mblfs.Get(test_var_name)->Get(eliminated_var_name);
207  // The AddMult method in mfem::BilinearForm is not defined for non-legacy assembly
208  mfem::Vector lf_prev(lf->Size());
209  mblf->Mult(*_eliminated_variables.Get(eliminated_var_name), lf_prev);
210  *lf -= lf_prev;
211  }
212  }
213  }
214 }
215 
216 void
217 EquationSystem::FormLinearSystem(mfem::OperatorHandle & op,
218  mfem::BlockVector & trueX,
219  mfem::BlockVector & trueRHS)
220 {
221 
222  switch (_assembly_level)
223  {
224  case mfem::AssemblyLevel::LEGACY:
225  FormLegacySystem(op, trueX, trueRHS);
226  break;
227  default:
228  mooseAssert(_test_var_names.size() == 1,
229  "Non-legacy assembly is only supported for single-variable systems");
230  mooseAssert(
231  _test_var_names.size() == _trial_var_names.size(),
232  "Non-legacy assembly is only supported for single test and trial variable systems");
233  FormSystem(op, trueX, trueRHS);
234  }
235 }
236 
237 void
238 EquationSystem::FormSystem(mfem::OperatorHandle & op,
239  mfem::BlockVector & trueX,
240  mfem::BlockVector & trueRHS)
241 {
242  auto & test_var_name = _test_var_names.at(0);
243  auto blf = _blfs.Get(test_var_name);
244  auto lf = _lfs.Get(test_var_name);
245  mfem::BlockVector aux_x, aux_rhs;
246  mfem::OperatorPtr aux_a;
247 
248  blf->FormLinearSystem(
249  _ess_tdof_lists.at(0), *(_var_ess_constraints.at(0)), *lf, aux_a, aux_x, aux_rhs);
250 
251  trueX.GetBlock(0) = aux_x;
252  trueRHS.GetBlock(0) = aux_rhs;
253  trueX.SyncFromBlocks();
254  trueRHS.SyncFromBlocks();
255 
256  op.Reset(aux_a.Ptr());
257  aux_a.SetOperatorOwner(false);
258 }
259 
260 void
264  jac_mblfs,
266  std::vector<mfem::Array<int>> & ess_tdof_lists,
267  std::vector<std::unique_ptr<mfem::ParGridFunction>> & var_ess_constraints,
268  mfem::OperatorHandle & op,
269  mfem::BlockVector & trueX,
270  mfem::BlockVector & trueRHS)
271 {
272  // Allocate block operator
273  DeleteAllBlocks();
274  _h_blocks.SetSize(_test_var_names.size(), _test_var_names.size());
275  // Form diagonal blocks.
276  for (const auto i : index_range(_test_var_names))
277  {
278  auto & test_var_name = _test_var_names.at(i);
279  auto blf = jac_blfs.Get(test_var_name);
280  auto lf = rhs_lfs.Get(test_var_name);
281  mfem::Vector aux_x, aux_rhs;
282  mfem::HypreParMatrix * aux_a = new mfem::HypreParMatrix;
283  blf->FormLinearSystem(
284  ess_tdof_lists.at(i), *(var_ess_constraints.at(i)), *lf, *aux_a, aux_x, aux_rhs);
285  _h_blocks(i, i) = aux_a;
286  trueX.GetBlock(i) = aux_x;
287  trueRHS.GetBlock(i) = aux_rhs;
288  }
289 
290  // Form off-diagonal blocks
291  for (const auto i : index_range(_test_var_names))
292  {
293  auto test_var_name = _test_var_names.at(i);
294  for (const auto j : index_range(_trial_var_names))
295  {
296  auto trial_var_name = _trial_var_names.at(j);
297 
298  mfem::Vector aux_x, aux_rhs;
299  mfem::ParLinearForm aux_lf(_test_pfespaces.at(i));
300  aux_lf = 0.0;
301  if (jac_mblfs.Has(test_var_name) && jac_mblfs.Get(test_var_name)->Has(trial_var_name))
302  {
303  auto mblf = jac_mblfs.Get(test_var_name)->Get(trial_var_name);
304  mfem::HypreParMatrix * aux_a = new mfem::HypreParMatrix;
305  mblf->FormRectangularLinearSystem(ess_tdof_lists.at(j),
306  ess_tdof_lists.at(i),
307  *(var_ess_constraints.at(j)),
308  aux_lf,
309  *aux_a,
310  aux_x,
311  aux_rhs);
312  _h_blocks(i, j) = aux_a;
313  trueRHS.GetBlock(i) += aux_rhs;
314  }
315  }
316  }
317  // Sync memory
318  trueX.SyncFromBlocks();
319  trueRHS.SyncFromBlocks();
320 
321  // Create monolithic matrix
322  op.Reset(mfem::HypreParMatrixFromBlocks(_h_blocks));
323 }
324 
325 void
326 EquationSystem::FormLegacySystem(mfem::OperatorHandle & op,
327  mfem::BlockVector & trueX,
328  mfem::BlockVector & trueRHS)
329 {
331 }
332 
333 void
334 EquationSystem::BuildJacobian(mfem::BlockVector & trueX, mfem::BlockVector & trueRHS)
335 {
336  height = trueX.Size();
337  width = trueRHS.Size();
338  FormLinearSystem(_jacobian, trueX, trueRHS);
339 }
340 
341 void
342 EquationSystem::Mult(const mfem::Vector & x, mfem::Vector & residual) const
343 {
344  _jacobian->Mult(x, residual);
345  x.HostRead();
346  residual.HostRead();
347 }
348 
349 mfem::Operator &
350 EquationSystem::GetGradient(const mfem::Vector &) const
351 {
352  return *_jacobian;
353 }
354 
355 void
356 EquationSystem::RecoverFEMSolution(mfem::BlockVector & trueX,
357  Moose::MFEM::GridFunctions & gridfunctions)
358 {
359  for (const auto i : index_range(_trial_var_names))
360  {
361  auto & trial_var_name = _trial_var_names.at(i);
362  trueX.GetBlock(i).SyncAliasMemory(trueX);
363  gridfunctions.Get(trial_var_name)->Distribute(&(trueX.GetBlock(i)));
364  }
365 }
366 
367 void
369 {
370  // Register linear forms
371  for (const auto i : index_range(_test_var_names))
372  {
373  auto test_var_name = _test_var_names.at(i);
374  _lfs.Register(test_var_name, std::make_shared<mfem::ParLinearForm>(_test_pfespaces.at(i)));
375  _lfs.GetRef(test_var_name) = 0.0;
376  }
377 
378  for (auto & test_var_name : _test_var_names)
379  {
380  // Apply kernels
381  auto lf = _lfs.GetShared(test_var_name);
382  ApplyDomainLFIntegrators(test_var_name, lf, _kernels_map);
384  lf->Assemble();
385  }
386 
387  // Apply essential boundary conditions
389 
390  // Eliminate trivially eliminated variables by subtracting contributions from linear forms
392 }
393 
394 void
396 {
397  // Register bilinear forms
398  for (const auto i : index_range(_test_var_names))
399  {
400  auto test_var_name = _test_var_names.at(i);
401  _blfs.Register(test_var_name, std::make_shared<mfem::ParBilinearForm>(_test_pfespaces.at(i)));
402 
403  // Apply kernels
404  auto blf = _blfs.GetShared(test_var_name);
405  blf->SetAssemblyLevel(_assembly_level);
406  ApplyBoundaryBLFIntegrators<mfem::ParBilinearForm>(
407  test_var_name, test_var_name, blf, _integrated_bc_map);
408  ApplyDomainBLFIntegrators<mfem::ParBilinearForm>(
409  test_var_name, test_var_name, blf, _kernels_map);
410  // Assemble
411  blf->Assemble();
412  }
413 }
414 
415 void
417 {
418  // Register mixed bilinear forms. Note that not all combinations may
419  // have a kernel.
420 
421  // Create mblf for each test/coupled variable pair with an added kernel.
422  // Mixed bilinear forms with coupled variables that are not trial variables are
423  // associated with contributions from eliminated variables.
424  for (const auto i : index_range(_test_var_names))
425  {
426  auto test_var_name = _test_var_names.at(i);
427  auto test_mblfs = std::make_shared<Moose::MFEM::NamedFieldsMap<mfem::ParMixedBilinearForm>>();
428  for (const auto j : index_range(_coupled_var_names))
429  {
430  const auto & coupled_var_name = _coupled_var_names.at(j);
431  auto mblf = std::make_shared<mfem::ParMixedBilinearForm>(_coupled_pfespaces.at(j),
432  _test_pfespaces.at(i));
433  // Register MixedBilinearForm if kernels exist for it, and assemble
434  // kernels
435  if (_kernels_map.Has(test_var_name) &&
436  _kernels_map.Get(test_var_name)->Has(coupled_var_name) &&
437  test_var_name != coupled_var_name)
438  {
439  mblf->SetAssemblyLevel(_assembly_level);
440  // Apply all mixed kernels with this test/trial pair
441  ApplyDomainBLFIntegrators<mfem::ParMixedBilinearForm>(
442  coupled_var_name, test_var_name, mblf, _kernels_map);
443  // Assemble mixed bilinear forms
444  mblf->Assemble();
445  // Register mixed bilinear forms associated with a single trial variable
446  // for the current test variable
447  test_mblfs->Register(coupled_var_name, mblf);
448  }
449  }
450  // Register all mixed bilinear form sets associated with a single test
451  // variable
452  _mblfs.Register(test_var_name, test_mblfs);
453  }
454 }
455 
456 void
458 {
462 }
463 
465  const Moose::MFEM::TimeDerivativeMap & time_derivative_map)
466  : _dt_coef(1.0), _time_derivative_map(time_derivative_map)
467 {
468 }
469 
470 void
472  mfem::AssemblyLevel assembly_level)
473 {
474  EquationSystem::Init(gridfunctions, assembly_level);
475  for (auto & test_var_name : _test_var_names)
476  _td_var_ess_constraints.emplace_back(
477  std::make_unique<mfem::ParGridFunction>(gridfunctions.Get(test_var_name)->ParFESpace()));
478 }
479 
480 void
482 {
483  if (fabs(dt - _dt_coef.constant) > 1.0e-12 * dt)
484  {
485  _dt_coef.constant = dt;
486  for (auto test_var_name : _test_var_names)
487  {
488  auto blf = _blfs.Get(test_var_name);
489  blf->Update();
490  blf->Assemble();
491  }
492  }
493 }
494 
495 void
497 {
498  // The TimeDependentEquationSystem operator expects to act on a vector of variable time
499  // derivatives, so the trial variable must be the time derivative of the 'base' variable. The base
500  // variable (test_var_name) without derivatives applied must also be coupled in implicit
501  // timestepping schemes for the elimination of 'old' variable values from the previous timestep
502  for (const auto & test_var_name : _test_var_names)
503  {
504  AddCoupledVariableNameIfMissing(test_var_name);
506  }
507 
508  // If a coupled variable does not have an equation associated with it,
509  // add it to the set of eliminated variables.
510  for (const auto & test_var_name : _test_var_names)
511  {
512  const auto time_derivative_test_var_name =
514  for (const auto & coupled_var_name : _coupled_var_names)
515  {
516  if (time_derivative_test_var_name != coupled_var_name &&
517  !VectorContainsName(_eliminated_var_names, coupled_var_name))
518  _eliminated_var_names.push_back(coupled_var_name);
519  }
520  if (!VectorContainsName(_trial_var_names, time_derivative_test_var_name))
521  _trial_var_names.push_back(time_derivative_test_var_name);
522  }
523 }
524 
525 void
526 TimeDependentEquationSystem::AddKernel(std::shared_ptr<MFEMKernel> kernel)
527 {
528  const auto & trial_var_name = kernel->getTrialVariableName();
529  const auto & test_var_name = kernel->getTestVariableName();
530 
531  if (_time_derivative_map.isTimeDerivative(trial_var_name))
532  {
533  AddTestVariableNameIfMissing(test_var_name);
534  AddCoupledVariableNameIfMissing(trial_var_name);
535 
536  // Register new kernels map if not present for the test variable
537  if (!_td_kernels_map.Has(test_var_name))
538  {
539  auto kernel_field_map =
540  std::make_shared<Moose::MFEM::NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>>();
541  _td_kernels_map.Register(test_var_name, std::move(kernel_field_map));
542  }
543  if (!_td_kernels_map.Get(test_var_name)->Has(trial_var_name))
544  {
545  auto kernels = std::make_shared<std::vector<std::shared_ptr<MFEMKernel>>>();
546  _td_kernels_map.Get(test_var_name)->Register(trial_var_name, std::move(kernels));
547  }
548  _td_kernels_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(kernel));
549  }
550  else
551  {
553  }
554 }
555 
556 void
558 {
560 
561  // Build and assemble bilinear forms acting on time derivatives
562  for (const auto i : index_range(_test_var_names))
563  {
564  auto test_var_name = _test_var_names.at(i);
565 
566  _td_blfs.Register(test_var_name,
567  std::make_shared<mfem::ParBilinearForm>(_test_pfespaces.at(i)));
568 
569  // Apply kernels to td_blf
570  auto td_blf = _td_blfs.GetShared(test_var_name);
571  td_blf->SetAssemblyLevel(_assembly_level);
572  ApplyBoundaryBLFIntegrators<mfem::ParBilinearForm>(
573  test_var_name, test_var_name, td_blf, _integrated_bc_map);
574  ApplyDomainBLFIntegrators<mfem::ParBilinearForm>(
576  test_var_name,
577  td_blf,
579 
580  // Recover and scale integrators from blf. This is to apply the dt*du/dt contributions from the
581  // operator on the trial variable in the implicit integration scheme
582  auto blf = _blfs.GetShared(test_var_name);
583  ScaleAndAddBLFIntegrators(blf, td_blf, _dt_coef.constant);
584  // Assemble form
585  td_blf->Assemble();
586  }
587 }
588 
589 void
591 {
593  // Register mixed bilinear forms. Note that not all combinations may
594  // have a kernel.
595 
596  // Create mblf for each test/trial variable pair with an added kernel
597  for (const auto i : index_range(_test_var_names))
598  {
599  const auto & test_var_name = _test_var_names.at(i);
600  auto test_td_mblfs =
601  std::make_shared<Moose::MFEM::NamedFieldsMap<mfem::ParMixedBilinearForm>>();
602  for (const auto j : index_range(_trial_var_names))
603  {
604  const auto & trial_var_name = _trial_var_names.at(j);
605  auto td_mblf = std::make_shared<mfem::ParMixedBilinearForm>(_test_pfespaces.at(j),
606  _test_pfespaces.at(i));
607  // Register MixedBilinearForm if kernels exist for it, and assemble
608  // kernels
609  if (_td_kernels_map.Has(test_var_name) &&
610  _td_kernels_map.Get(test_var_name)->Has(trial_var_name) &&
611  _time_derivative_map.getTimeDerivativeName(test_var_name) != trial_var_name)
612  {
613  // Apply all mixed kernels with this test/trial pair
614  ApplyDomainBLFIntegrators<mfem::ParMixedBilinearForm>(
615  trial_var_name, test_var_name, td_mblf, _td_kernels_map);
616  }
617  // Recover and scale integrators from the mblf acting on the time integral of the trial
618  // variable corresponding to coupled_var_name. This is to apply the dt*du/dt contributions
619  // from the operator on the trial variable in the implicit integration scheme
620  const auto & trial_var_time_integral_name =
622  if (_mblfs.Has(test_var_name) && _mblfs.Get(test_var_name)->Has(trial_var_time_integral_name))
623  {
624  // Recover and scale integrators from mblf. This is to apply the dt*du/dt contributions
625  // from the operator on the trial variable in the implicit integration scheme
626  auto mblf = _mblfs.Get(test_var_name)->GetShared(trial_var_time_integral_name);
627  ScaleAndAddBLFIntegrators(mblf, td_mblf, _dt_coef.constant);
628  }
629  // If implicit contributions exist, scale them by timestep and add to td_mblf
630  if (td_mblf->GetDBFI()->Size() || td_mblf->GetBBFI()->Size())
631  {
632  // Assemble mixed bilinear form
633  td_mblf->SetAssemblyLevel(_assembly_level);
634  td_mblf->Assemble();
635  // Register mixed bilinear forms associated with a single trial variable
636  // for the current test variable
637  test_td_mblfs->Register(trial_var_name, td_mblf);
638  }
639  }
640  // Register all mixed bilinear forms associated with a single test variable
641  _td_mblfs.Register(test_var_name, test_td_mblfs);
642  }
643 }
644 
645 void
647 {
648  _ess_tdof_lists.resize(_test_var_names.size());
649  for (const auto i : index_range(_test_var_names))
650  {
651  const auto & test_var_name = _test_var_names.at(i);
652  const auto time_derivative_test_var_name =
654  mfem::ParGridFunction & trial_gf = *(_var_ess_constraints.at(i));
655  mfem::ParGridFunction & trial_gf_time_derivative = *(_td_var_ess_constraints.at(i));
656  mfem::Array<int> global_ess_markers(trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max());
657  global_ess_markers = 0;
658  // Set strongly constrained DoFs of trial_gf on essential boundaries and add markers for all
659  // essential boundaries to the global_ess_markers array
660  EquationSystem::ApplyEssentialBC(test_var_name, trial_gf, global_ess_markers);
661  // Update solution values on Dirichlet values to be in terms of du/dt instead of u
662  *_td_var_ess_constraints.at(i).get() = *(_var_ess_constraints.at(i).get());
663  *_td_var_ess_constraints.at(i).get() -= *_eliminated_variables.Get(test_var_name);
664  *_td_var_ess_constraints.at(i).get() /= _dt_coef.constant;
665  // Apply any remaining Dirichlet BCs specified directly on du/dt
667  time_derivative_test_var_name, trial_gf_time_derivative, global_ess_markers);
668  trial_gf.ParFESpace()->GetEssentialTrueDofs(global_ess_markers, _ess_tdof_lists.at(i));
669  }
670 }
671 
672 void
674 {
675  // Eliminate contributions from variables at previous timestep.
676  for (const auto i : index_range(_test_var_names))
677  {
678  auto & test_var_name = _test_var_names.at(i);
679  auto blf = _blfs.Get(test_var_name);
680  auto lf = _lfs.Get(test_var_name);
681  // if implicit, add contribution to linear form from terms involving state
682  // The AddMult method in mfem::BilinearForm is not defined for non-legacy assembly
683  mfem::Vector lf_prev(lf->Size());
684  blf->Mult(*_eliminated_variables.Get(test_var_name), lf_prev);
685  *lf -= lf_prev;
686  }
687  // Eliminate contributions from other coupled variables.
689 }
690 
691 void
693  mfem::BlockVector & truedXdt,
694  mfem::BlockVector & trueRHS)
695 {
696  // Form linear system for operator acting on vector of du/dt
698  _td_blfs, _td_mblfs, _lfs, _ess_tdof_lists, _td_var_ess_constraints, op, truedXdt, trueRHS);
699 }
700 
701 void
702 TimeDependentEquationSystem::FormSystem(mfem::OperatorHandle & op,
703  mfem::BlockVector & truedXdt,
704  mfem::BlockVector & trueRHS)
705 {
706  auto & test_var_name = _test_var_names.at(0);
707  auto td_blf = _td_blfs.Get(test_var_name);
708  auto lf = _lfs.Get(test_var_name);
709 
710  // Form linear system for operator acting on vector of du/dt
711  mfem::OperatorPtr aux_a;
712  mfem::Vector aux_x, aux_rhs;
713  td_blf->FormLinearSystem(
714  _ess_tdof_lists.at(0), *(_td_var_ess_constraints.at(0)), *lf, aux_a, aux_x, aux_rhs);
715 
716  truedXdt.GetBlock(0) = aux_x;
717  trueRHS.GetBlock(0) = aux_rhs;
718  truedXdt.SyncFromBlocks();
719  trueRHS.SyncFromBlocks();
720 
721  // Create monolithic matrix
722  op.Reset(aux_a.Ptr());
723  aux_a.SetOperatorOwner(false);
724 }
725 
726 void
728 {
730 }
731 
732 } // namespace Moose::MFEM
733 
734 #endif
std::string name(const ElemQuality q)
mfem::ConstantCoefficient _dt_coef
Coefficient for timestep scaling.
virtual void EliminateCoupledVariables()
Perform trivial eliminations of coupled variables lacking corresponding test variables.
virtual void AddKernel(std::shared_ptr< MFEMKernel > kernel) override
Add kernels.
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:42
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 RecoverFEMSolution(mfem::BlockVector &trueX, Moose::MFEM::GridFunctions &gridfunctions)
Update variable from solution vector after solve.
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.
void AssembleJacobian(Moose::MFEM::NamedFieldsMap< mfem::ParBilinearForm > &jac_blfs, Moose::MFEM::NamedFieldsMap< Moose::MFEM::NamedFieldsMap< mfem::ParMixedBilinearForm >> &jac_mblfs, Moose::MFEM::NamedFieldsMap< mfem::ParLinearForm > &rhs_lfs, std::vector< mfem::Array< int >> &ess_tdof_lists, std::vector< std::unique_ptr< mfem::ParGridFunction >> &var_ess_constraints, mfem::OperatorHandle &op, mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
Form Jacobian operator based on on- and off-diagonal bilinear form contributions, populate solution a...
std::vector< mfem::ParFiniteElementSpace * > _coupled_pfespaces
Pointers to finite element spaces associated with coupled variables.
Moose::MFEM::NamedFieldsMap< Moose::MFEM::NamedFieldsMap< std::vector< std::shared_ptr< MFEMKernel > > > > _td_kernels_map
bool isTimeDerivative(const std::string &time_derivative_var_name) const
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:323
virtual void FormSystem(mfem::OperatorHandle &op, mfem::BlockVector &truedXdt, mfem::BlockVector &trueRHS) override
void ApplyBoundaryLFIntegrators(const std::string &test_var_name, std::shared_ptr< mfem::ParLinearForm > form, Moose::MFEM::NamedFieldsMap< Moose::MFEM::NamedFieldsMap< std::vector< std::shared_ptr< MFEMIntegratedBC >>>> &integrated_bc_map)
bool VectorContainsName(const std::vector< std::string > &the_vector, const std::string &name) const
std::vector< std::string > _eliminated_var_names
Names of all coupled variables without a corresponding test variable.
Moose::MFEM::NamedFieldsMap< Moose::MFEM::NamedFieldsMap< mfem::ParMixedBilinearForm > > _mblfs
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.
Lightweight adaptor over a std::map relating names of GridFunctions with the name of their time deriv...
const std::string & getTimeIntegralName(const std::string &time_derivative_var_name) const
std::vector< mfem::Array< int > > _ess_tdof_lists
Moose::MFEM::NamedFieldsMap< Moose::MFEM::NamedFieldsMap< mfem::ParMixedBilinearForm > > _td_mblfs
Lightweight adaptor over an std::map from strings to pointer to T.
mfem::AssemblyLevel _assembly_level
auto max(const L &left, const R &right)
Moose::MFEM::NamedFieldsMap< std::vector< std::shared_ptr< MFEMEssentialBC > > > _essential_bc_map
Arrays to store essential BCs to act on each component of weak form.
std::vector< mfem::ParFiniteElementSpace * > _test_pfespaces
Pointers to finite element spaces associated with test variables.
virtual void Init(Moose::MFEM::GridFunctions &gridfunctions, mfem::AssemblyLevel assembly_level) override
Initialise.
virtual void SetTimeStep(mfem::real_t dt)
void Mult(const mfem::Vector &u, mfem::Vector &residual) const override
Compute residual y = Mu.
std::vector< std::string > _trial_var_names
Subset of _coupled_var_names of all variables corresponding to gridfunctions with degrees of freedom ...
std::vector< std::unique_ptr< mfem::ParGridFunction > > _td_var_ess_constraints
Gridfunctions holding essential constraints from Dirichlet BCs.
virtual void BuildMixedBilinearForms() override
Build mixed bilinear forms (off-diagonal Jacobian contributions)
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...
virtual void EliminateCoupledVariables() override
Perform trivial eliminations of coupled variables lacking corresponding test variables.
virtual void FormLegacySystem(mfem::OperatorHandle &op, mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
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...
virtual void SetTrialVariableNames() override
Set trial variable names from subset of coupled variables that have an associated test variable...
mfem::OperatorHandle _jacobian
virtual void Init(Moose::MFEM::GridFunctions &gridfunctions, mfem::AssemblyLevel assembly_level)
Initialise.
void ScaleAndAddBLFIntegrators(std::shared_ptr< FormType > source_blf, std::shared_ptr< FormType > target_blf, mfem::real_t scale_factor)
Fetch all integrators on a source bilinear form, scale them by a real factor, and add to a second tar...
TimeDependentEquationSystem(const Moose::MFEM::TimeDerivativeMap &time_derivative_map)
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.
virtual void FormLegacySystem(mfem::OperatorHandle &op, mfem::BlockVector &truedXdt, mfem::BlockVector &trueRHS) override
virtual void BuildMixedBilinearForms()
Build mixed bilinear forms (off-diagonal Jacobian contributions)
mfem::Array2D< const mfem::HypreParMatrix * > _h_blocks
virtual void ApplyEssentialBCs() override
Update all essentially constrained true DoF markers and values on boundaries.
virtual void SetTrialVariableNames()
Set trial variable names from subset of coupled variables that have an associated test variable...
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 system, with essential boundary conditions accounted for.
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.
Moose::MFEM::NamedFieldsMap< Moose::MFEM::NamedFieldsMap< std::vector< std::shared_ptr< MFEMIntegratedBC > > > > _integrated_bc_map
Arrays to store integrated BCs to act on each component of weak form.
void DeleteAllBlocks()
Deletes the HypreParMatrix associated with any pointer stored in _h_blocks, and then proceeds to dele...
Moose::MFEM::NamedFieldsMap< mfem::ParBilinearForm > _td_blfs
Containers to store contributions to weak form of the form (F du/dt, v)
virtual void BuildLinearForms()
Build linear forms and eliminate constrained DoFs.
IntRange< T > make_range(T beg, T end)
const Moose::MFEM::TimeDerivativeMap & _time_derivative_map
Map between variable names and their time derivatives.
virtual void BuildJacobian(mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
Build linear system, with essential boundary conditions accounted for.
void ApplyDomainLFIntegrators(const std::string &test_var_name, std::shared_ptr< mfem::ParLinearForm > form, Moose::MFEM::NamedFieldsMap< Moose::MFEM::NamedFieldsMap< std::vector< std::shared_ptr< MFEMKernel >>>> &kernels_map)
virtual void ApplyEssentialBCs()
Update all essentially constrained true DoF markers and values on boundaries.
virtual void ApplyEssentialBC(const std::string &test_var_name, mfem::ParGridFunction &trial_gf, mfem::Array< int > &global_ess_markers)
Apply essential BC(s) associated with test_var_name to set true DoFs of trial_gf and update markers o...
Moose::MFEM::NamedFieldsMap< mfem::ParLinearForm > _lfs
virtual void BuildBilinearForms() override
Build bilinear forms (diagonal Jacobian contributions)
Moose::MFEM::NamedFieldsMap< Moose::MFEM::NamedFieldsMap< std::vector< std::shared_ptr< MFEMKernel > > > > _kernels_map
Arrays to store kernels to act on each component of weak form.
T & GetRef(const std::string &field_name) const
Returns a reference to a field.
mfem::Operator & GetGradient(const mfem::Vector &u) const override
Compute J = M + grad_H(u)
virtual void FormSystem(mfem::OperatorHandle &op, mfem::BlockVector &trueX, mfem::BlockVector &trueRHS)
Moose::MFEM::NamedFieldsMap< mfem::ParBilinearForm > _blfs
virtual void AddIntegratedBC(std::shared_ptr< MFEMIntegratedBC > kernel)
auto index_range(const T &sizable)
const std::string & getTimeDerivativeName(const std::string &var_name) const