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 // 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
109void
111{
112 setCurrentExecuteOnFlag(exec_type);
113 executeMFEMObjects(exec_type);
114
115 ExternalProblem::execute(exec_type);
116}
117
118void
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
128void
129MFEMProblem::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
139void
140MFEMProblem::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
147void
148MFEMProblem::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
156void
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
241}
242
243void
244MFEMProblem::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 =
255 std::dynamic_pointer_cast<Moose::MFEM::EquationSystem>(getProblemData().eqn_system);
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 =
266 std::dynamic_pointer_cast<Moose::MFEM::ComplexEquationSystem>(getProblemData().eqn_system);
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 =
277 std::dynamic_pointer_cast<Moose::MFEM::ComplexEquationSystem>(getProblemData().eqn_system);
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 =
288 std::dynamic_pointer_cast<Moose::MFEM::EquationSystem>(getProblemData().eqn_system);
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
301void
302MFEMProblem::addMaterial(const std::string &, const std::string &, InputParameters &)
303{
305 "MFEM materials must be added through the 'FunctorMaterials' block and not 'Materials'");
306}
307
308void
309MFEMProblem::addFunctorMaterial(const std::string & material_name,
310 const std::string & name,
311 InputParameters & parameters)
312{
313 addObject<MFEMFunctorMaterial>(material_name, name, parameters);
314}
315
316void
317MFEMProblem::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
334void
335MFEMProblem::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
357void
358MFEMProblem::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
371void
372MFEMProblem::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
391void
392MFEMProblem::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
431void
432MFEMProblem::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
441void
442MFEMProblem::addAuxKernel(const std::string & kernel_name,
443 const std::string & name,
444 InputParameters & parameters)
445{
446 addObject<MFEMExecutedObject>(kernel_name, name, parameters);
447}
448
449void
450MFEMProblem::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 =
461 std::dynamic_pointer_cast<Moose::MFEM::ComplexEquationSystem>(getProblemData().eqn_system);
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 =
471 std::dynamic_pointer_cast<Moose::MFEM::EquationSystem>(getProblemData().eqn_system);
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
480void
481MFEMProblem::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
492void
493MFEMProblem::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
504void
505MFEMProblem::addRealComponentToBC(const std::string & kernel_name,
506 const std::string & name,
507 InputParameters & parameters)
508{
509 auto parent_ptr = std::dynamic_pointer_cast<MFEMComplexIntegratedBC>(
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");
514 auto bc_ptr = std::dynamic_pointer_cast<MFEMIntegratedBC>(
515 addObject<MFEMBoundaryCondition>(kernel_name, name + "_real", parameters).front());
516 parent_ptr->setRealBC(bc_ptr);
517}
518
519void
520MFEMProblem::addImagComponentToBC(const std::string & kernel_name,
521 const std::string & name,
522 InputParameters & parameters)
523{
524 auto parent_ptr = std::dynamic_pointer_cast<MFEMComplexIntegratedBC>(
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");
529 auto bc_ptr = std::dynamic_pointer_cast<MFEMIntegratedBC>(
530 addObject<MFEMBoundaryCondition>(kernel_name, name + "_imag", parameters).front());
531 parent_ptr->setImagBC(bc_ptr);
532}
533
534int
535vectorFunctionDim(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
547const 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
586void
587MFEMProblem::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
626void
627MFEMProblem::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
643void
644MFEMProblem::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
719void
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
730std::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
745void
746MFEMProblem::rebalanceMesh(mfem::ParMesh & pmesh)
747{
748 if (pmesh.Nonconforming())
749 {
750 pmesh.Rebalance();
753 }
754}
755
756void
758{
759 for (const auto & fe_space_pair : _problem_data.fespaces)
760 fe_space_pair.second->Update();
761}
762
763void
765{
766 for (const auto & gridfunction_pair : _problem_data.gridfunctions)
767 gridfunction_pair.second->Update();
768}
769
770std::vector<VariableName>
775
776MFEMMesh &
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
784const MFEMMesh &
786{
787 return const_cast<MFEMProblem *>(this)->mesh();
788}
789
790void
791MFEMProblem::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
800void
801MFEMProblem::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
809void
810MFEMProblem::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
820void
821MFEMProblem::addInitialCondition(const std::string & ic_name,
822 const std::string & name,
823 InputParameters & parameters)
824{
825 addObject<MFEMExecutedObject>(ic_name, name, parameters);
826}
827
828void
830{
831 std::vector<MFEMExecutedObject *> objects;
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
873std::string
874MFEMProblem::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
883 if (getProblemData().jacobian_solver)
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
893bool
894MFEMProblem::hasMFEMObject(const std::string & system, const std::string & name) const
895{
896 std::vector<MooseObject *> objs;
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
const std::vector< std::string > SCALAR_FUNCS
int vectorFunctionDim(const std::string &type, const InputParameters &parameters)
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.
virtual MooseMesh & mesh() override
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.
MFEMMesh inherits a MOOSE mesh class which allows us to work with other MOOSE objects.
Definition MFEMMesh.h:21
std::optional< std::reference_wrapper< std::string const > > getMeshDisplacementVariable() const
Returns an optional reference to displacement variable name.
Definition MFEMMesh.h:63
unsigned int dimension() const override
Returns MeshBase::mesh_dimension(), (not MeshBase::spatial_dimension()!) of the underlying libMesh me...
Definition MFEMMesh.h:75
std::shared_ptr< mfem::ParMesh > getMFEMParMeshPtr()
Copy a shared_ptr to the mfem::ParMesh object.
Definition MFEMMesh.h:40
void displace(mfem::GridFunction const &displacement)
Displace the nodes of the mesh by the given displacement.
Definition MFEMMesh.C:145
virtual std::vector< VariableName > getAuxVariableNames()
Returns all the variable names from the auxiliary system base.
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 addQuadratureFunction(const std::string &type, const std::string &name, InputParameters &parameters)
Add an MFEM QuadratureFunction-backed coefficient to the problem.
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 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:881
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