https://mooseframework.inl.gov
MFEMProblem.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 "MFEMProblem.h"
13 #include "MFEMVariable.h"
14 #include "MFEMIndicator.h"
15 #include "MFEMSubMesh.h"
16 #include "MFEMFunctorMaterial.h"
17 #include "MFEMExecutedObject.h"
18 #include "MFEMVectorUtils.h"
19 #include "MFEMFESpaceHierarchy.h"
20 #include "Postprocessor.h"
21 #include "VectorPostprocessor.h"
23 #include "DependencyResolver.h"
24 #include "MooseUtils.h"
25 #include "DataIO.h"
26 
27 #include "libmesh/string_to_enum.h"
28 
29 #include <vector>
30 #include <algorithm>
31 #include <map>
32 #include <deque>
33 #include <sstream>
34 
35 registerMooseObject("MooseApp", MFEMProblem);
36 
37 namespace
38 {
39 std::vector<MFEMSolverName>
40 getMFEMSolverDependencies(const InputParameters & parameters)
41 {
42  std::vector<MFEMSolverName> dependencies;
43 
44  for (const auto & [param_name, _] : parameters)
45  {
46  if (parameters.isPrivate(param_name))
47  continue;
48 
49  if (auto * name = parameters.queryParam<MFEMSolverName>(param_name))
50  dependencies.push_back(*name);
51  else if (auto * names = parameters.queryParam<std::vector<MFEMSolverName>>(param_name))
52  dependencies.insert(dependencies.end(), names->begin(), names->end());
53  }
54 
55  return dependencies;
56 }
57 }
58 
61 {
63  params.addClassDescription("Problem type for building and solving the finite element problem "
64  "using the MFEM finite element library.");
65  MooseEnum numeric_types("real complex", "real");
66  params.addParam<MooseEnum>("numeric_type", numeric_types, "Number type used for the problem");
67 
68  return params;
69 }
70 
72  : ExternalProblem(params),
73  _num_type{static_cast<int>(getParam<MooseEnum>("numeric_type"))},
74  _solution_state_data(declareRestartableDataWithContext<Moose::MFEM::SolutionState>(
75  "mfem_solution_state", &_problem_data))
76 {
77  // Initialise Hypre for all MFEM problems.
78  mfem::Hypre::Init();
79  // Disable multithreading for all MFEM problems (including any libMesh or MFEM subapps).
81 #ifdef LIBMESH_HAVE_OPENMP
82  omp_set_num_threads(1);
83 #endif
84  setMesh();
85 }
86 
87 void
89 {
91 
92  std::vector<MFEMExecutedObject *> objects;
93  theWarehouse()
94  .query()
95  .condition<AttribSystem>("MFEMExecutedObject")
96  .condition<AttribThread>(0)
97  .queryInto(objects);
98  for (auto * const object : objects)
99  object->initialSetup();
100 
101  // MFEM indicators create their estimators during addIndicator(); markers still need an explicit
102  // setup pass because they are no longer initialized through the libMesh/MOOSE user-object path.
103  std::vector<MFEMRefinementMarker *> markers;
104  theWarehouse().query().condition<AttribSystem>("Marker").queryInto(markers);
105  for (auto marker : markers)
106  marker->initialSetup();
107 }
108 
109 void
111 {
112  setCurrentExecuteOnFlag(exec_type);
113  executeMFEMObjects(exec_type);
114 
115  ExternalProblem::execute(exec_type);
116 }
117 
118 void
120 {
121  auto pmesh = mesh().getMFEMParMeshPtr();
122  getProblemData().pmesh = pmesh;
123  getProblemData().comm = pmesh->GetComm();
124  getProblemData().num_procs = pmesh->GetNRanks();
125  getProblemData().myid = pmesh->GetMyRank();
126 }
127 
128 void
129 MFEMProblem::addIndicator(const std::string & indicator_type,
130  const std::string & name,
131  InputParameters & parameters)
132 {
133  auto estimator = addObject<MFEMIndicator>(indicator_type, name, parameters).front();
134 
135  // construct the estimator itself
136  estimator->createEstimator();
137 }
138 
139 void
140 MFEMProblem::addMarker(const std::string & marker_type,
141  const std::string & name,
142  InputParameters & parameters)
143 {
144  getProblemData().refiner = addObject<MFEMRefinementMarker>(marker_type, name, parameters).front();
145 }
146 
147 void
148 MFEMProblem::addMFEMSolver(const std::string & solver_type,
149  const std::string & name,
150  InputParameters & parameters)
151 {
152  mooseAssert(!_mfem_solver_definitions.count(name), "Multiple MFEM solvers named '" + name + "'.");
154 }
155 
156 void
158 {
159  if (_mfem_solver_definitions.empty())
160  return;
161 
163 
164  for (auto & [solver_name, definition] : _mfem_solver_definitions)
165  {
166  const auto dependencies = getMFEMSolverDependencies(*definition.parameters);
167  if (dependencies.empty())
168  resolver.addNode(solver_name);
169 
170  for (const auto & dependency_name : dependencies)
171  {
172  auto dependency_it = _mfem_solver_definitions.find(dependency_name);
173  if (dependency_it == _mfem_solver_definitions.end())
174  mooseError("MFEM solver '",
175  solver_name,
176  "' references MFEM solver '",
177  dependency_name,
178  "', but no solver with that name was provided in the [Solvers] block.");
179 
180  dependency_it->second.referenced = true;
181  resolver.addEdge(dependency_name, solver_name);
182  }
183  }
184 
185  const std::vector<std::string> * sorted_solver_names = nullptr;
186  try
187  {
188  sorted_solver_names = &resolver.getSortedValues();
189  }
191  {
192  mooseError("Cyclic MFEM solver dependency detected: ",
193  MooseUtils::join(e.getCyclicDependencies(), " <- "));
194  }
195 
196  auto & problem_data = getProblemData();
197  mooseAssert(!problem_data.jacobian_solver, "MFEM linear solver driver already assigned");
198  mooseAssert(!problem_data.nonlinear_solver, "MFEM nonlinear solver driver already assigned");
199 
200  for (const auto & solver_name : *sorted_solver_names)
201  {
202  auto & definition = libmesh_map_find(_mfem_solver_definitions, solver_name);
203  auto solver =
204  addObject<Moose::MFEM::SolverBase>(definition.type, solver_name, *definition.parameters)
205  .front();
206 
207  if (definition.referenced)
208  continue;
209 
210  if (auto lin_solver = std::dynamic_pointer_cast<Moose::MFEM::LinearSolverBase>(solver))
211  {
212  if (problem_data.jacobian_solver)
213  mooseError("Multiple MFEM linear solver drivers provided. '",
214  problem_data.jacobian_solver->name(),
215  "' and '",
216  lin_solver->name(),
217  "' are not referenced by another MFEM solver.");
218  problem_data.jacobian_solver = lin_solver;
219  }
220  else if (auto nonlinear_solver =
221  std::dynamic_pointer_cast<Moose::MFEM::NonlinearSolverBase>(solver);
222  nonlinear_solver)
223  {
224  if (problem_data.nonlinear_solver)
225  mooseError("Multiple MFEM nonlinear solver drivers provided. '",
226  problem_data.nonlinear_solver->name(),
227  "' and '",
228  nonlinear_solver->name(),
229  "' are not referenced by another MFEM solver.");
230  problem_data.nonlinear_solver = nonlinear_solver;
231  }
232  else
233  mooseError("Unsupported MFEM solver object type '",
234  solver->type(),
235  "' for solver '",
236  solver->name(),
237  "'.");
238  }
239 
240  _mfem_solver_definitions.clear();
241 }
242 
243 void
244 MFEMProblem::addBoundaryCondition(const std::string & bc_name,
245  const std::string & name,
246  InputParameters & parameters)
247 {
248  auto bc = addObject<MFEMBoundaryCondition>(bc_name, name, parameters).front();
249  const auto & mfem_bc = *bc;
250 
251  if (dynamic_cast<const MFEMIntegratedBC *>(&mfem_bc))
252  {
253  auto integrated_bc = std::dynamic_pointer_cast<MFEMIntegratedBC>(bc);
254  auto eqsys =
256  if (eqsys)
257  eqsys->AddIntegratedBC(std::move(integrated_bc));
258  else
259  mooseError("Cannot add integrated BC with name '" + name +
260  "' because there is no corresponding equation system.");
261  }
262  else if (dynamic_cast<const MFEMComplexIntegratedBC *>(&mfem_bc))
263  {
264  auto integrated_bc = std::dynamic_pointer_cast<MFEMComplexIntegratedBC>(bc);
265  auto eqsys =
267  if (eqsys)
268  eqsys->AddComplexIntegratedBC(std::move(integrated_bc));
269  else
270  mooseError("Cannot add complex integrated BC with name '" + name +
271  "' because there is no corresponding equation system.");
272  }
273  else if (dynamic_cast<const MFEMComplexEssentialBC *>(&mfem_bc))
274  {
275  auto essential_bc = std::dynamic_pointer_cast<MFEMComplexEssentialBC>(bc);
276  auto eqsys =
278  if (eqsys)
279  eqsys->AddComplexEssentialBCs(std::move(essential_bc));
280  else
281  mooseError("Cannot add boundary condition with name '" + name +
282  "' because there is no corresponding equation system.");
283  }
284  else if (dynamic_cast<const MFEMEssentialBC *>(&mfem_bc))
285  {
286  auto essential_bc = std::dynamic_pointer_cast<MFEMEssentialBC>(bc);
287  auto eqsys =
289  if (eqsys)
290  eqsys->AddEssentialBC(std::move(essential_bc));
291  else
292  mooseError("Cannot add boundary condition with name '" + name +
293  "' because there is no corresponding equation system.");
294  }
295  else
296  {
297  mooseError("Unsupported bc of type '", bc_name, "' and name '", name, "' detected.");
298  }
299 }
300 
301 void
302 MFEMProblem::addMaterial(const std::string &, const std::string &, InputParameters &)
303 {
304  mooseError(
305  "MFEM materials must be added through the 'FunctorMaterials' block and not 'Materials'");
306 }
307 
308 void
309 MFEMProblem::addFunctorMaterial(const std::string & material_name,
310  const std::string & name,
311  InputParameters & parameters)
312 {
313  addObject<MFEMFunctorMaterial>(material_name, name, parameters);
314 }
315 
316 void
317 MFEMProblem::addFESpace(const std::string & type,
318  const std::string & name,
319  InputParameters & parameters)
320 {
321  if (getProblemData().fespace_hierarchies.Has(name))
322  mooseError("Cannot add FESpace '",
323  name,
324  "': an MFEMFESpaceHierarchy with the same name already exists. "
325  "FESpaces and FESpaceHierarchies share the fespaces namespace.");
326 
327  auto & mfem_fespace = *addObject<MFEMFESpace>(type, name, parameters).front();
328 
329  // Register fespace and associated fe collection.
330  getProblemData().fecs.Register(name, mfem_fespace.getFEC());
331  getProblemData().fespaces.Register(name, mfem_fespace.getFESpace());
332 }
333 
334 void
335 MFEMProblem::addFESpaceHierarchy(const std::string & type,
336  const std::string & name,
337  InputParameters & parameters)
338 {
339  if (getProblemData().fespaces.Has(name))
340  mooseError("Cannot add MFEMFESpaceHierarchy '",
341  name,
342  "': a FESpace with the same name already exists. "
343  "FESpaces and FESpaceHierarchies share the fespaces namespace.");
344 
345  auto hierarchy_obj = addObject<MFEMFESpaceHierarchy>(type, name, parameters).front();
346  auto hierarchy_shared = hierarchy_obj->getHierarchyShared();
347  // Register the hierarchy for co-ownership by solvers.
348  getProblemData().fespace_hierarchies.Register(name, hierarchy_shared);
349  // Register the finest-level FESpace in fespaces under the hierarchy name so that
350  // variables can say `fespace = <hierarchy_name>` without a separate FESpace definition.
351  // The aliasing shared_ptr keeps the hierarchy alive as long as this entry lives.
352  auto finest = std::shared_ptr<mfem::ParFiniteElementSpace>(
353  hierarchy_shared, &hierarchy_obj->getHierarchy().GetFinestFESpace());
355 }
356 
357 void
358 MFEMProblem::validateVariableNumericType(const std::string & var_type,
359  const std::string & var_name) const
360 {
361  const bool variable_is_complex = var_type == "MFEMComplexVariable";
362  const bool problem_is_complex = _num_type == NumericType::COMPLEX;
363  if (variable_is_complex != problem_is_complex)
364  paramError("numeric_type",
365  "The problem numeric type does not match primary MFEM variable '",
366  var_name,
367  "', which is ",
368  variable_is_complex ? "complex." : "real.");
369 }
370 
371 void
372 MFEMProblem::addVariable(const std::string & var_type,
373  const std::string & var_name,
374  InputParameters & parameters)
375 {
376  validateVariableNumericType(var_type, var_name);
377  addGridFunction(var_type, var_name, parameters);
378  // MOOSE variables store DoFs for the trial variable and its time derivatives up to second order;
379  // MFEM GridFunctions store data for only one set of DoFs each, so we must add additional
380  // GridFunctions for time derivatives.
381  if (isTransient())
382  {
383  const auto time_derivative_var_name =
384  getMFEMObject<MFEMVariable>("MooseVariableBase", var_name).getTimeDerivativeName();
386  time_derivative_var_name);
387  addGridFunction(var_type, time_derivative_var_name, parameters);
388  }
389 }
390 
391 void
392 MFEMProblem::addGridFunction(const std::string & var_type,
393  const std::string & var_name,
394  InputParameters & parameters)
395 {
396 
397  if (var_type == "MFEMVariable" || var_type == "MFEMComplexVariable")
398  {
399  // Add MFEM variable directly.
400  if (var_type == "MFEMComplexVariable")
401  addObject<MFEMComplexVariable>(var_type, var_name, parameters);
402  else
403  addObject<MFEMVariable>(var_type, var_name, parameters);
404  }
405  else
406  {
407  // Add MOOSE variable.
408  ExternalProblem::addVariable(var_type, var_name, parameters);
409 
410  // Add MFEM variable indirectly ("gridfunction").
412  addObject<MFEMVariable>("MFEMVariable", var_name, mfem_variable_params);
413  }
414 
415  // Register gridfunction.
416  if (var_type == "MFEMComplexVariable")
417  {
418  MFEMComplexVariable & mfem_variable =
419  getMFEMObject<MFEMComplexVariable>("MooseVariableBase", var_name);
421  mfem_variable.declareCoefficients();
422  }
423  else // must be real, but may have been set up indirectly from a MOOSE variable
424  {
425  MFEMVariable & mfem_variable = getMFEMObject<MFEMVariable>("MooseVariableBase", var_name);
426  getProblemData().gridfunctions.Register(var_name, mfem_variable.getGridFunction());
427  mfem_variable.declareCoefficients();
428  }
429 }
430 
431 void
432 MFEMProblem::addAuxVariable(const std::string & var_type,
433  const std::string & var_name,
434  InputParameters & parameters)
435 {
436  // We handle MFEM AuxVariables just like MFEM Variables, except
437  // we do not add additional GridFunctions for time derivatives.
438  addGridFunction(var_type, var_name, parameters);
439 }
440 
441 void
442 MFEMProblem::addAuxKernel(const std::string & kernel_name,
443  const std::string & name,
444  InputParameters & parameters)
445 {
446  addObject<MFEMExecutedObject>(kernel_name, name, parameters);
447 }
448 
449 void
450 MFEMProblem::addKernel(const std::string & kernel_name,
451  const std::string & name,
452  InputParameters & parameters)
453 {
454  auto kernel = addObject<MFEMKernel>(kernel_name, name, parameters).front();
455  const auto & kernel_object = *kernel;
456 
457  if (dynamic_cast<const MFEMComplexKernel *>(&kernel_object))
458  {
459  auto complex_kernel = std::dynamic_pointer_cast<MFEMComplexKernel>(kernel);
460  auto eqsys =
462  if (eqsys)
463  eqsys->AddComplexKernel(std::move(complex_kernel));
464  else
465  mooseError("Cannot add complex kernel with name '" + name +
466  "' because there is no corresponding equation system.");
467  }
468  else
469  {
470  auto eqsys =
472  if (eqsys)
473  eqsys->AddKernel(std::move(kernel));
474  else
475  mooseError("Cannot add kernel with name '" + name +
476  "' because there is no corresponding equation system.");
477  }
478 }
479 
480 void
481 MFEMProblem::addRealComponentToKernel(const std::string & kernel_name,
482  const std::string & name,
483  InputParameters & parameters)
484 {
485  auto parent_ptr = std::dynamic_pointer_cast<MFEMComplexKernel>(
486  getMFEMObject<MFEMComplexKernel>("Kernel", name).getSharedPtr());
487  parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
488  auto kernel_ptr = addObject<MFEMKernel>(kernel_name, name + "_real", parameters).front();
489  parent_ptr->setRealKernel(kernel_ptr);
490 }
491 
492 void
493 MFEMProblem::addImagComponentToKernel(const std::string & kernel_name,
494  const std::string & name,
495  InputParameters & parameters)
496 {
497  auto parent_ptr = std::dynamic_pointer_cast<MFEMComplexKernel>(
498  getMFEMObject<MFEMComplexKernel>("Kernel", name).getSharedPtr());
499  parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
500  auto kernel_ptr = addObject<MFEMKernel>(kernel_name, name + "_imag", parameters).front();
501  parent_ptr->setImagKernel(kernel_ptr);
502 }
503 
504 void
505 MFEMProblem::addRealComponentToBC(const std::string & kernel_name,
506  const std::string & name,
507  InputParameters & parameters)
508 {
510  getMFEMObject<MFEMComplexIntegratedBC>("BoundaryCondition", name).getSharedPtr());
511  parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
512  parameters.set<std::vector<BoundaryName>>("boundary") =
513  parent_ptr->getParam<std::vector<BoundaryName>>("boundary");
515  addObject<MFEMBoundaryCondition>(kernel_name, name + "_real", parameters).front());
516  parent_ptr->setRealBC(bc_ptr);
517 }
518 
519 void
520 MFEMProblem::addImagComponentToBC(const std::string & kernel_name,
521  const std::string & name,
522  InputParameters & parameters)
523 {
525  getMFEMObject<MFEMComplexIntegratedBC>("BoundaryCondition", name).getSharedPtr());
526  parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
527  parameters.set<std::vector<BoundaryName>>("boundary") =
528  parent_ptr->getParam<std::vector<BoundaryName>>("boundary");
530  addObject<MFEMBoundaryCondition>(kernel_name, name + "_imag", parameters).front());
531  parent_ptr->setImagBC(bc_ptr);
532 }
533 
534 int
535 vectorFunctionDim(const std::string & type, const InputParameters & parameters)
536 {
537  if (parameters.isParamSetByUser("expression_z"))
538  return 3;
539  if (parameters.isParamSetByUser("expression_y") || type == "LevelSetOlssonVortex")
540  return 2;
541  if (parameters.isParamSetByUser("expression_x"))
542  return 1;
543 
544  return 3;
545 }
546 
547 const std::vector<std::string> SCALAR_FUNCS = {"Axisymmetric2D3DSolutionFunction",
548  "BicubicSplineFunction",
549  "CoarsenedPiecewiseLinear",
550  "CompositeFunction",
551  "ConstantFunction",
552  "ImageFunction",
553  "ParsedFunction",
554  "ParsedGradFunction",
555  "PeriodicFunction",
556  "PiecewiseBilinear",
557  "PiecewiseConstant",
558  "PiecewiseConstantFromCSV",
559  "PiecewiseLinear",
560  "PiecewiseLinearFromVectorPostprocessor",
561  "PiecewiseMultiInterpolation",
562  "PiecewiseMulticonstant",
563  "SolutionFunction",
564  "SplineFunction",
565  "FunctionSeries",
566  "LevelSetOlssonBubble",
567  "LevelSetOlssonPlane",
568  "NearestReporterCoordinatesFunction",
569  "ParameterMeshFunction",
570  "ParsedOptimizationFunction",
571  "FourierNoise",
572  "MovingPlanarFront",
573  "MultiControlDrumFunction",
574  "Grad2ParsedFunction",
575  "GradParsedFunction",
576  "ScaledAbsDifferenceDRLRewardFunction",
577  "CircularAreaHydraulicDiameterFunction",
578  "CosineHumpFunction",
579  "CosineTransitionFunction",
580  "CubicTransitionFunction",
581  "GeneralizedCircumference",
582  "PiecewiseFunction",
583  "TimeRampFunction"},
584  VECTOR_FUNCS = {"ParsedVectorFunction", "LevelSetOlssonVortex"};
585 
586 void
587 MFEMProblem::addFunction(const std::string & type,
588  const std::string & name,
589  InputParameters & parameters)
590 {
592  auto & func = getFunction(name);
593  // FIXME: Do we want to have optimised versions for when functions
594  // are only of space or only of time.
595  if (std::find(SCALAR_FUNCS.begin(), SCALAR_FUNCS.end(), type) != SCALAR_FUNCS.end())
596  {
597  getCoefficients().declareScalar<mfem::FunctionCoefficient>(
598  name,
599  [&func](const mfem::Vector & p, mfem::real_t t) -> mfem::real_t
600  { return func.value(t, Moose::MFEM::libMeshPointFromMFEMVector(p)); });
601  }
602  else if (std::find(VECTOR_FUNCS.begin(), VECTOR_FUNCS.end(), type) != VECTOR_FUNCS.end())
603  {
605  getCoefficients().declareVector<mfem::VectorFunctionCoefficient>(
606  name,
607  dim,
608  [&func, dim](const mfem::Vector & p, mfem::real_t t, mfem::Vector & u)
609  {
610  libMesh::RealVectorValue vector_value =
611  func.vectorValue(t, Moose::MFEM::libMeshPointFromMFEMVector(p));
612  for (int i = 0; i < dim; i++)
613  {
614  u[i] = vector_value(i);
615  }
616  });
617  }
618  else if ("MFEMParsedFunction" != type)
619  {
620  mooseWarning("Could not identify whether function ",
621  type,
622  " is scalar or vector; no MFEM coefficient object created.");
623  }
624 }
625 
626 void
627 MFEMProblem::addPostprocessor(const std::string & type,
628  const std::string & name,
629  InputParameters & parameters)
630 {
631  if (parameters.getSystemAttributeName() == "MFEMExecutedObject")
632  {
633  checkUserObjectNameCollision(name, "Postprocessor");
634  addObject<MFEMExecutedObject>(type, name, parameters);
636  getCoefficients().declareScalar<mfem::FunctionCoefficient>(
637  name, [&val](const mfem::Vector &) -> mfem::real_t { return val; });
638  }
639  else
641 }
642 
643 void
644 MFEMProblem::addVectorPostprocessor(const std::string & type,
645  const std::string & name,
646  InputParameters & parameters)
647 {
648  if (parameters.getSystemAttributeName() == "MFEMExecutedObject")
649  {
650  checkUserObjectNameCollision(name, "VectorPostprocessor");
651  addObject<MFEMExecutedObject>(type, name, parameters);
652  }
653  else
655 }
656 
659 {
660 
661  InputParameters fespace_params = _factory.getValidParams("MFEMGenericFESpace");
662  InputParameters variable_params = _factory.getValidParams("MFEMVariable");
663 
664  const auto family = Utility::string_to_enum<FEFamily>(parameters.get<MooseEnum>("family"));
665  auto order = static_cast<int>(parameters.get<MooseEnum>("order"));
666  const auto dim = mesh().dimension();
667 
668  std::string space;
669  int vdim = 1;
670 
671  switch (family)
672  {
673  case FEFamily::LAGRANGE:
674  space = "H1";
675  break;
676  case FEFamily::NEDELEC_ONE:
677  space = "ND";
678  break;
679  case FEFamily::RAVIART_THOMAS:
680  space = "RT";
681  --order;
682  break;
683  case FEFamily::MONOMIAL:
684  case FEFamily::L2_LAGRANGE:
685  space = "L2";
686  break;
687  case FEFamily::LAGRANGE_VEC:
688  space = "H1";
689  vdim = dim;
690  break;
691  case FEFamily::MONOMIAL_VEC:
692  case FEFamily::L2_LAGRANGE_VEC:
693  space = "L2";
694  vdim = dim;
695  break;
696  default:
697  mooseError("Unable to set MFEM FESpace for MOOSE variable");
698  break;
699  }
700 
701  // Create fespace name. If this already exists, we will reuse this for
702  // the mfem variable ("gridfunction"). If using AMR, this implies all
703  // variables sharing the fespace are affected.
704  const auto fec_name = space + "_" + std::to_string(dim) + "D_P" + std::to_string(order);
705  const auto fes_name = fec_name + "_X" + std::to_string(vdim);
706 
707  // Set all fespace parameters.
708  fespace_params.set<std::string>("fec_name") = fec_name;
709  fespace_params.set<int>("vdim") = vdim;
710 
711  if (!hasMFEMObject("MFEMFESpace", fes_name))
712  addFESpace("MFEMGenericFESpace", fes_name, fespace_params);
713 
714  variable_params.set<MFEMFESpaceName>("fespace") = fes_name;
715 
716  return variable_params;
717 }
718 
719 void
721 {
722  // Displace mesh
723  if (mesh().shouldDisplace())
724  {
725  mesh().displace(static_cast<mfem::GridFunction const &>(*getMeshDisplacementGridFunction()));
726  // TODO: update FESpaces GridFunctions etc for transient solves
727  }
728 }
729 
730 std::optional<std::reference_wrapper<mfem::ParGridFunction const>>
732 {
733  // If C++23 transform were available this would be easier
734  auto const displacement_variable = mesh().getMeshDisplacementVariable();
735  if (displacement_variable)
736  {
737  return *_problem_data.gridfunctions.Get(displacement_variable.value());
738  }
739  else
740  {
741  return std::nullopt;
742  }
743 }
744 
745 void
746 MFEMProblem::rebalanceMesh(mfem::ParMesh & pmesh)
747 {
748  if (pmesh.Nonconforming())
749  {
750  pmesh.Rebalance();
751  updateFESpaces();
753  }
754 }
755 
756 void
758 {
759  for (const auto & fe_space_pair : _problem_data.fespaces)
760  fe_space_pair.second->Update();
761 }
762 
763 void
765 {
766  for (const auto & gridfunction_pair : _problem_data.gridfunctions)
767  gridfunction_pair.second->Update();
768 }
769 
770 std::vector<VariableName>
772 {
774 }
775 
776 MFEMMesh &
778 {
779  mooseAssert(ExternalProblem::mesh().type() == "MFEMMesh",
780  "Please choose the MFEMMesh mesh type for an MFEMProblem\n");
781  return static_cast<MFEMMesh &>(_mesh);
782 }
783 
784 const MFEMMesh &
786 {
787  return const_cast<MFEMProblem *>(this)->mesh();
788 }
789 
790 void
791 MFEMProblem::addSubMesh(const std::string & var_type,
792  const std::string & var_name,
793  InputParameters & parameters)
794 {
795  auto & mfem_submesh = *addObject<MFEMSubMesh>(var_type, var_name, parameters).front();
796  // Register submesh.
797  getProblemData().submeshes.Register(var_name, mfem_submesh.getSubMesh());
798 }
799 
800 void
801 MFEMProblem::addQuadratureFunction(const std::string & type,
802  const std::string & name,
803  InputParameters & parameters)
804 {
805  // The object declares its coefficient with the CoefficientManager on construction.
806  addObject<MFEMObject>(type, name, parameters);
807 }
808 
809 void
810 MFEMProblem::addTransfer(const std::string & transfer_name,
811  const std::string & name,
812  InputParameters & parameters)
813 {
814  if (parameters.getBase() == "MFEMSubMeshTransfer")
815  addObject<MFEMExecutedObject>(transfer_name, name, parameters);
816  else
818 }
819 
820 void
821 MFEMProblem::addInitialCondition(const std::string & ic_name,
822  const std::string & name,
823  InputParameters & parameters)
824 {
825  addObject<MFEMExecutedObject>(ic_name, name, parameters);
826 }
827 
828 void
830 {
831  std::vector<MFEMExecutedObject *> objects;
832  theWarehouse()
833  .query()
834  .condition<AttribSystem>("MFEMExecutedObject")
835  .condition<AttribExecOns>(exec_type)
836  .condition<AttribThread>(0)
837  .queryInto(objects);
838 
839  std::map<std::string, const MFEMExecutedObject *> suppliers;
840  for (auto * const object : objects)
841  for (const auto & item : object->getSuppliedItems())
842  {
843  const auto [it, inserted] = suppliers.emplace(item, object);
844  if (!inserted && it->second != object)
845  mooseError("MFEM executed-object dependency ambiguity on ",
846  exec_type,
847  ": both '",
848  it->second->name(),
849  "' and '",
850  object->name(),
851  "' supply '",
852  item,
853  "'.");
854  }
855 
856  for (auto * const object : objects)
857  {
858  object->initialize();
859  object->execute();
860  object->finalize();
861 
862  if (auto * const pp = dynamic_cast<const Postprocessor *>(object))
863  {
864  _reporter_data.finalize(pp->PPName());
865  setPostprocessorValueByName(pp->PPName(), pp->getValue());
866  }
867 
868  if (auto * const vpp = dynamic_cast<VectorPostprocessor *>(object))
869  _reporter_data.finalize(vpp->PPName());
870  }
871 }
872 
873 std::string
874 MFEMProblem::solverTypeString(const unsigned int libmesh_dbg_var(solver_sys_num))
875 {
876  mooseAssert(solver_sys_num == 0, "No support for multi-system with MFEM right now");
877 
878  std::vector<std::string> solvers;
879 
880  if (getProblemData().nonlinear_solver)
881  solvers.push_back(MooseUtils::prettyCppType(getProblemData().nonlinear_solver.get()));
882 
884  {
885  solvers.push_back(MooseUtils::prettyCppType(getProblemData().jacobian_solver.get()));
886  if (const auto * prec = getProblemData().jacobian_solver->GetPreconditioner())
887  solvers.push_back(MooseUtils::prettyCppType(prec));
888  }
889 
890  return solvers.empty() ? "None" : MooseUtils::stringJoin(solvers);
891 }
892 
893 bool
894 MFEMProblem::hasMFEMObject(const std::string & system, const std::string & name) const
895 {
896  std::vector<MooseObject *> objs;
897  theWarehouse()
898  .query()
899  .condition<AttribSystem>(system)
900  .condition<AttribThread>(0)
901  .condition<AttribName>(name)
902  .queryInto(objs);
903  return !objs.empty();
904 }
905 
906 #endif
std::map< std::string, MFEMSolverDefinition > _mfem_solver_definitions
Solver definitions recorded by AddMFEMSolverAction before the dependency resolver constructs them...
Definition: MFEMProblem.h:410
std::shared_ptr< mfem::ParMesh > pmesh
virtual void resolveMFEMSolvers()
Construct recorded MFEM solvers in dependency order and select the problem driver solver(s)...
Definition: MFEMProblem.C:157
void addGridFunction(const std::string &var_type, const std::string &var_name, InputParameters &parameters)
Adds one MFEM GridFunction to be used in the MFEM solve.
Definition: MFEMProblem.C:392
Constructs and stores an mfem::ParComplexGridFunction object.
std::shared_ptr< mfem::ParComplexGridFunction > getComplexGridFunction() const
Returns a shared pointer to the constructed gridfunction.
void addMarker(const std::string &type, const std::string &name, InputParameters &parameters) override
Override of FEProblemBase::addMarker.
Definition: MFEMProblem.C:140
virtual void addTransfer(const std::string &transfer_name, const std::string &name, InputParameters &parameters)
Add a Transfer to the problem.
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
unsigned int dimension() const override
Returns MeshBase::mesh_dimension(), (not MeshBase::spatial_dimension()!) of the underlying libMesh me...
Definition: MFEMMesh.h:75
Factory & _factory
The Factory for building objects.
Definition: SubProblem.h:1062
libMesh::Point libMeshPointFromMFEMVector(const mfem::Vector &vec)
Convert an MFEM position vector to a libMesh::Point.
virtual void AddKernel(std::shared_ptr< MFEMKernel > kernel)
Add kernels.
void addRealComponentToBC(const std::string &kernel_name, const std::string &name, InputParameters &parameters)
Adds a real component BC to the parent MFEMComplexIntegratedBC.
Definition: MFEMProblem.C:505
Moose::MFEM::ComplexGridFunctions cmplx_gridfunctions
MFEMProblemData & getProblemData()
Method to get the current MFEMProblemData object storing the current data specifying the FE problem...
Definition: MFEMProblem.h:271
const std::string & name() const
Definition: MooseEnumItem.h:35
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition: MooseBase.h:457
const std::vector< std::string > SCALAR_FUNCS
Definition: MFEMProblem.C:547
void addImagComponentToBC(const std::string &kernel_name, const std::string &name, InputParameters &parameters)
Adds an imaginary component BC to the parent MFEMComplexIntegratedBC.
Definition: MFEMProblem.C:520
void setPostprocessorValueByName(const PostprocessorName &name, const PostprocessorValue &value, std::size_t t_index=0)
Set the value of a PostprocessorValue.
void addPostprocessor(const std::string &type, const std::string &name, InputParameters &parameters) override
Override of ExternalProblem::addPostprocessor.
Definition: MFEMProblem.C:627
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
Combine two vector parameters into a single vector of pairs.
NumericType _num_type
The numeric representation currently active for this problem.
Definition: MFEMProblem.h:402
std::optional< std::reference_wrapper< std::string const > > getMeshDisplacementVariable() const
Returns an optional reference to displacement variable name.
Definition: MFEMMesh.h:63
void addFunction(const std::string &type, const std::string &name, InputParameters &parameters) override
Override of ExternalProblem::addFunction.
Definition: MFEMProblem.C:587
const std::vector< T > & getSortedValues()
This function also returns dependency resolved values but with a simpler single vector interface...
InputParameters addMFEMFESpaceFromMOOSEVariable(InputParameters &moosevar_params)
Method used to get an mfem FEC depending on the variable family specified in the input file...
Definition: MFEMProblem.C:658
static InputParameters validParams()
Return the input parameters used to construct an MFEM problem.
Definition: MFEMProblem.C:60
virtual MFEMMesh & mesh() override
Overwritten mesh() method from base MooseMesh to retrieve the correct mesh type, in this case MFEMMes...
Definition: MFEMProblem.C:777
const InputParameters & parameters() const
Get the parameters of the object.
Definition: MooseBase.h:131
registerMooseObject("MooseApp", MFEMProblem)
virtual void addVariable(const std::string &var_type, const std::string &var_name, InputParameters &parameters) override
Override of ExternalProblem::addVariable.
Definition: MFEMProblem.C:372
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
InputParameters getValidParams(const std::string &name) const
Get valid parameters for the object.
Definition: Factory.C:68
static InputParameters validParams()
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
std::shared_ptr< Moose::MFEM::LinearSolverBase > jacobian_solver
Owns the weak-form mathematics of a MOOSE MFEM problem.
void executeMFEMObjects(const ExecFlagType &exec_type)
Execute MFEM executed objects scheduled on the supplied execute flag.
Definition: MFEMProblem.C:829
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition: Moose.h:165
std::string solverTypeString(unsigned int solver_sys_num) override
Return solver type as a human readable string.
Definition: MFEMProblem.C:874
virtual void AddEssentialBC(std::shared_ptr< MFEMEssentialBC > bc)
Add BC associated with essentially constrained DoFs on boundaries.
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
These are reworked from https://stackoverflow.com/a/11003103.
std::shared_ptr< MooseObject > getSharedPtr()
Get another shared pointer to this object that has the same ownership group.
Definition: MooseObject.C:70
int vectorFunctionDim(const std::string &type, const InputParameters &parameters)
Definition: MFEMProblem.C:535
void setCurrentExecuteOnFlag(const ExecFlagType &)
void displaceMesh()
Displace the mesh, if mesh displacement is enabled.
Definition: MFEMProblem.C:720
MFEMProblemData _problem_data
Aggregated MFEM-side state for meshes, spaces, variables, coefficients, and solvers.
Definition: MFEMProblem.h:397
void addEdge(const T &a, const T &b)
Add an edge between nodes &#39;a&#39; and &#39;b&#39;.
const std::string & getBase() const
Moose::MFEM::FESpaces fespaces
Moose::MFEM::SubMeshes submeshes
Constructs and stores an mfem::ParGridFunction object.
Definition: MFEMVariable.h:19
Moose::MFEM::FECollections fecs
bool hasMFEMObject(const std::string &system, const std::string &name) const
Determine whether an MFEM object with the supplied system and name exists.
Definition: MFEMProblem.C:894
void AddComplexEssentialBCs(std::shared_ptr< MFEMComplexEssentialBC > bc)
Add complex essential BCs.
void checkUserObjectNameCollision(const std::string &name, const std::string &type) const
Check for name collision between different user objects.
virtual Function & getFunction(const std::string &name, const THREAD_ID tid=0)
virtual void addPostprocessor(const std::string &pp_name, const std::string &name, InputParameters &parameters)
void mooseWarning(Args &&... args) const
virtual void execute(const ExecFlagType &exec_type)
Convenience function for performing execution of MOOSE systems.
void rebalanceMesh(mfem::ParMesh &pmesh)
Rebalance the (necessarily nonconforming) mesh.
Definition: MFEMProblem.C:746
ReporterData _reporter_data
const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:103
mfem::Coefficient & declareScalar(const std::string &name, const std::string &existing_or_literal)
Declare an alias to an existing scalar coefficient or, if it does not exist, try interpreting the nam...
void addTimeDerivativeAssociation(const std::string &var_name, const std::string &time_derivative_var_name)
TheWarehouse & theWarehouse() const
void addQuadratureFunction(const std::string &type, const std::string &name, InputParameters &parameters)
Add an MFEM QuadratureFunction-backed coefficient to the problem.
Definition: MFEMProblem.C:801
Moose::MFEM::FESpaceHierarchies fespace_hierarchies
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...
mfem::VectorCoefficient & declareVector(const std::string &name, const std::string &existing_or_literal)
Declare an alias to an existing vector coefficientor or, if it does not exist, try interpreting the n...
virtual void addFunction(const std::string &type, const std::string &name, InputParameters &parameters)
Real PostprocessorValue
various MOOSE typedefs
Definition: MooseTypes.h:230
virtual void addVectorPostprocessor(const std::string &pp_name, const std::string &name, InputParameters &parameters)
Moose::MFEM::CoefficientManager & getCoefficients()
Method to get the PropertyManager object for storing material properties and converting them to MFEM ...
Definition: MFEMProblem.h:265
MooseMesh & _mesh
void addMaterial(const std::string &material_name, const std::string &name, InputParameters &parameters) override
Definition: MFEMProblem.C:302
virtual void initialSetup() override
Definition: MFEMProblem.C:88
const std::string & type() const
Get the type of this class.
Definition: MooseBase.h:93
void initialSetup() override
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:54
void setMesh()
Set the mesh used by MFEM.
Definition: MFEMProblem.C:119
virtual const SystemBase & systemBaseAuxiliary() const override
Return the auxiliary system object as a base class reference.
virtual void addMFEMSolver(const std::string &user_object_name, const std::string &name, InputParameters &parameters)
Method called in AddMFEMSolverAction which records a solver for later dependency-ordered construction...
Definition: MFEMProblem.C:148
void finalize(const std::string &object_name)
Helper function for performing post calculation actions via the ReporterContext objects.
Definition: ReporterData.C:48
void addAuxKernel(const std::string &kernel_name, const std::string &name, InputParameters &parameters) override
Override of ExternalProblem::addAuxKernel.
Definition: MFEMProblem.C:442
std::shared_ptr< mfem::ParMesh > getMFEMParMeshPtr()
Copy a shared_ptr to the mfem::ParMesh object.
Definition: MFEMMesh.h:40
std::shared_ptr< mfem::ParGridFunction > getGridFunction() const
Returns a shared pointer to the constructed gridfunction.
Definition: MFEMVariable.h:27
const PostprocessorValue & getPostprocessorValueByName(const PostprocessorName &name, std::size_t t_index=0) const
Get a read-only reference to the value associated with a Postprocessor that exists.
std::shared_ptr< Moose::MFEM::EquationSystem > eqn_system
void addFESpaceHierarchy(const std::string &type, const std::string &name, InputParameters &parameters)
Add an MFEMFESpaceHierarchy to the problem.
Definition: MFEMProblem.C:335
void AddComplexIntegratedBC(std::shared_ptr< MFEMComplexIntegratedBC > bc)
Add complex integrated BCs.
virtual void addVariable(const std::string &var_type, const std::string &var_name, InputParameters &params)
Canonical method for adding a non-linear variable.
std::shared_ptr< MFEMRefinementMarker > refiner
bool isParamSetByUser(const std::string &name) const
Method returns true if the parameter was set by the user.
void Register(const std::string &field_name, FieldArgs &&... args)
Construct new field with name field_name and register.
MFEMProblem(const InputParameters &params)
Construct an MFEM problem from the supplied parameters.
Definition: MFEMProblem.C:71
void displace(mfem::GridFunction const &displacement)
Displace the nodes of the mesh by the given displacement.
Definition: MFEMMesh.C:145
void addFunctorMaterial(const std::string &material_name, const std::string &name, InputParameters &parameters) override
Definition: MFEMProblem.C:309
Class for containing MooseEnum item information.
Definition: MooseEnumItem.h:18
void addNode(const T &a)
Add a node &#39;a&#39; to the graph.
void addFESpace(const std::string &type, const std::string &name, InputParameters &parameters)
Add an MFEM FESpace to the problem.
Definition: MFEMProblem.C:317
void updateGridFunctions()
Calls Update() on all gridfunctions.
Definition: MFEMProblem.C:764
const std::vector< T > & getCyclicDependencies() const
void addIndicator(const std::string &type, const std::string &name, InputParameters &parameters) override
Override of FEProblemBase::addIndicator.
Definition: MFEMProblem.C:129
MFEMMesh inherits a MOOSE mesh class which allows us to work with other MOOSE objects.
Definition: MFEMMesh.h:20
virtual std::vector< VariableName > getAuxVariableNames()
Returns all the variable names from the auxiliary system base.
Definition: MFEMProblem.C:771
Query query()
query creates and returns an initialized a query object for querying objects from the warehouse...
Definition: TheWarehouse.h:467
void addBoundaryCondition(const std::string &bc_name, const std::string &name, InputParameters &parameters) override
Definition: MFEMProblem.C:244
const std::vector< std::string > VECTOR_FUNCS
Definition: MFEMProblem.C:584
virtual MooseMesh & mesh() override
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition: MooseBase.h:271
const std::vector< VariableName > & getVariableNames() const
Definition: SystemBase.h:863
void updateFESpaces()
Calls Update() on all FE spaces.
Definition: MFEMProblem.C:757
void addImagComponentToKernel(const std::string &kernel_name, const std::string &name, InputParameters &parameters)
Adds an imaginary component kernel to the parent MFEMComplexKernel.
Definition: MFEMProblem.C:493
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
const std::string & getSystemAttributeName() const
Get the system attribute name if it was registered.
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
QueryCache & condition(Args &&... args)
Adds a new condition to the query.
Definition: TheWarehouse.h:285
void addVectorPostprocessor(const std::string &type, const std::string &name, InputParameters &parameters) override
Add a vector postprocessor and register its vectors with the MFEM execution system.
Definition: MFEMProblem.C:644
void addKernel(const std::string &kernel_name, const std::string &name, InputParameters &parameters) override
Override of ExternalProblem::addKernel.
Definition: MFEMProblem.C:450
void validateVariableNumericType(const std::string &var_type, const std::string &var_name) const
Verify that a primary variable&#39;s numeric type matches the problem&#39;s equation system.
Definition: MFEMProblem.C:358
void declareCoefficients()
Declare default coefficients associated with this gridfunction.
Definition: MFEMVariable.C:88
virtual bool isTransient() const override
std::optional< std::reference_wrapper< mfem::ParGridFunction const > > getMeshDisplacementGridFunction()
Returns optional reference to the displacement GridFunction to apply to nodes.
Definition: MFEMProblem.C:731
Moose::MFEM::GridFunctions gridfunctions
void addAuxVariable(const std::string &var_type, const std::string &var_name, InputParameters &parameters) override
Override of ExternalProblem::addAuxVariable.
Definition: MFEMProblem.C:432
std::string stringJoin(const std::vector< std::string > &values, const std::string &separator=" ")
Concatenates value into a single string separated by separator.
void addRealComponentToKernel(const std::string &kernel_name, const std::string &name, InputParameters &parameters)
Adds a real component kernel to the parent MFEMComplexKernel.
Definition: MFEMProblem.C:481
virtual void AddIntegratedBC(std::shared_ptr< MFEMIntegratedBC > kernel)
void addSubMesh(const std::string &type, const std::string &name, InputParameters &parameters)
Add an MFEM SubMesh to the problem.
Definition: MFEMProblem.C:791
Moose::MFEM::TimeDerivativeMap time_derivative_map
virtual void execute(const ExecFlagType &exec_type) override
Convenience function for performing execution of MOOSE systems.
Definition: MFEMProblem.C:110
std::string prettyCppType(const std::string &cpp_type)
Definition: MooseUtils.C:1151
void addTransfer(const std::string &transfer_name, const std::string &name, InputParameters &parameters) override
Add transfers between MultiApps and/or MFEM SubMeshes.
Definition: MFEMProblem.C:810
void addInitialCondition(const std::string &ic_name, const std::string &name, InputParameters &parameters) override
Add an MFEM initial condition to the problem.
Definition: MFEMProblem.C:821
void AddComplexKernel(std::shared_ptr< MFEMComplexKernel > kernel)
Add complex kernels.