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