LCOV - code coverage report
Current view: top level - src/mfem/problem - MFEMProblem.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 329044 Lines: 367 421 87.2 %
Date: 2026-08-03 21:12:22 Functions: 43 48 89.6 %
Legend: Lines: hit not hit

          Line data    Source code
       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"
      22             : #include "MFEMNonlinearSolverBase.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        2164 : getMFEMSolverDependencies(const InputParameters & parameters)
      41             : {
      42        2164 :   std::vector<MFEMSolverName> dependencies;
      43             : 
      44       54210 :   for (const auto & [param_name, _] : parameters)
      45             :   {
      46       52046 :     if (parameters.isPrivate(param_name))
      47       32493 :       continue;
      48             : 
      49       19553 :     if (auto * name = parameters.queryParam<MFEMSolverName>(param_name))
      50        1001 :       dependencies.push_back(*name);
      51       18552 :     else if (auto * names = parameters.queryParam<std::vector<MFEMSolverName>>(param_name))
      52          11 :       dependencies.insert(dependencies.end(), names->begin(), names->end());
      53             :   }
      54             : 
      55        2164 :   return dependencies;
      56           0 : }
      57             : }
      58             : 
      59             : InputParameters
      60        8360 : MFEMProblem::validParams()
      61             : {
      62        8360 :   InputParameters params = ExternalProblem::validParams();
      63       16720 :   params.addClassDescription("Problem type for building and solving the finite element problem "
      64             :                              "using the MFEM finite element library.");
      65       33440 :   MooseEnum numeric_types("real complex", "real");
      66       25080 :   params.addParam<MooseEnum>("numeric_type", numeric_types, "Number type used for the problem");
      67             : 
      68       16720 :   return params;
      69        8360 : }
      70             : 
      71        2050 : MFEMProblem::MFEMProblem(const InputParameters & params)
      72             :   : ExternalProblem(params),
      73        4100 :     _num_type{static_cast<int>(getParam<MooseEnum>("numeric_type"))},
      74        2050 :     _solution_state_data(declareRestartableDataWithContext<Moose::MFEM::SolutionState>(
      75        4100 :         "mfem_solution_state", &_problem_data))
      76             : {
      77             :   // Initialise Hypre for all MFEM problems.
      78        2050 :   mfem::Hypre::Init();
      79             :   // Disable multithreading for all MFEM problems (including any libMesh or MFEM subapps).
      80        2050 :   libMesh::libMeshPrivateData::_n_threads = 1;
      81             : #ifdef LIBMESH_HAVE_OPENMP
      82        2050 :   omp_set_num_threads(1);
      83             : #endif
      84        2050 :   setMesh();
      85        2050 : }
      86             : 
      87             : void
      88        1694 : MFEMProblem::initialSetup()
      89             : {
      90        1694 :   ExternalProblem::initialSetup();
      91             : 
      92        1694 :   std::vector<MFEMExecutedObject *> objects;
      93        1694 :   theWarehouse()
      94        1694 :       .query()
      95        1694 :       .condition<AttribSystem>("MFEMExecutedObject")
      96        3388 :       .condition<AttribThread>(0)
      97        1694 :       .queryInto(objects);
      98        4985 :   for (auto * const object : objects)
      99        3309 :     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        1676 :   std::vector<MFEMRefinementMarker *> markers;
     104        1676 :   theWarehouse().query().condition<AttribSystem>("Marker").queryInto(markers);
     105        1702 :   for (auto marker : markers)
     106          26 :     marker->initialSetup();
     107        1676 : }
     108             : 
     109             : void
     110       11750 : MFEMProblem::execute(const ExecFlagType & exec_type)
     111             : {
     112       11750 :   setCurrentExecuteOnFlag(exec_type);
     113       11750 :   executeMFEMObjects(exec_type);
     114             : 
     115       11750 :   ExternalProblem::execute(exec_type);
     116       11750 : }
     117             : 
     118             : void
     119        2050 : MFEMProblem::setMesh()
     120             : {
     121        2050 :   auto pmesh = mesh().getMFEMParMeshPtr();
     122        2050 :   getProblemData().pmesh = pmesh;
     123        2050 :   getProblemData().comm = pmesh->GetComm();
     124        2050 :   getProblemData().num_procs = pmesh->GetNRanks();
     125        2050 :   getProblemData().myid = pmesh->GetMyRank();
     126        2050 : }
     127             : 
     128             : void
     129          26 : MFEMProblem::addIndicator(const std::string & indicator_type,
     130             :                           const std::string & name,
     131             :                           InputParameters & parameters)
     132             : {
     133          52 :   auto estimator = addObject<MFEMIndicator>(indicator_type, name, parameters).front();
     134             : 
     135             :   // construct the estimator itself
     136          26 :   estimator->createEstimator();
     137          26 : }
     138             : 
     139             : void
     140          26 : MFEMProblem::addMarker(const std::string & marker_type,
     141             :                        const std::string & name,
     142             :                        InputParameters & parameters)
     143             : {
     144          52 :   getProblemData().refiner = addObject<MFEMRefinementMarker>(marker_type, name, parameters).front();
     145          26 : }
     146             : 
     147             : void
     148        2164 : 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 + "'.");
     153        2164 :   _mfem_solver_definitions.emplace(name, MFEMSolverDefinition{solver_type, &parameters});
     154        2164 : }
     155             : 
     156             : void
     157        1696 : MFEMProblem::resolveMFEMSolvers()
     158             : {
     159        1696 :   if (_mfem_solver_definitions.empty())
     160         593 :     return;
     161             : 
     162        1103 :   DependencyResolver<std::string> resolver;
     163             : 
     164        3267 :   for (auto & [solver_name, definition] : _mfem_solver_definitions)
     165             :   {
     166        2164 :     const auto dependencies = getMFEMSolverDependencies(*definition.parameters);
     167        2164 :     if (dependencies.empty())
     168        1163 :       resolver.addNode(solver_name);
     169             : 
     170        3176 :     for (const auto & dependency_name : dependencies)
     171             :     {
     172        1012 :       auto dependency_it = _mfem_solver_definitions.find(dependency_name);
     173        1012 :       if (dependency_it == _mfem_solver_definitions.end())
     174           0 :         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        1012 :       dependency_it->second.referenced = true;
     181        1012 :       resolver.addEdge(dependency_name, solver_name);
     182             :     }
     183        2164 :   }
     184             : 
     185        1103 :   const std::vector<std::string> * sorted_solver_names = nullptr;
     186             :   try
     187             :   {
     188        1103 :     sorted_solver_names = &resolver.getSortedValues();
     189             :   }
     190           0 :   catch (CyclicDependencyException<std::string> & e)
     191             :   {
     192           0 :     mooseError("Cyclic MFEM solver dependency detected: ",
     193           0 :                MooseUtils::join(e.getCyclicDependencies(), " <- "));
     194           0 :   }
     195             : 
     196        1103 :   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        3263 :   for (const auto & solver_name : *sorted_solver_names)
     201             :   {
     202        2162 :     auto & definition = libmesh_map_find(_mfem_solver_definitions, solver_name);
     203             :     auto solver =
     204        6484 :         addObject<Moose::MFEM::SolverBase>(definition.type, solver_name, *definition.parameters)
     205        2160 :             .front();
     206             : 
     207        2160 :     if (definition.referenced)
     208        1010 :       continue;
     209             : 
     210        1150 :     if (auto lin_solver = std::dynamic_pointer_cast<Moose::MFEM::LinearSolverBase>(solver))
     211             :     {
     212        1094 :       if (problem_data.jacobian_solver)
     213           0 :         mooseError("Multiple MFEM linear solver drivers provided. '",
     214           0 :                    problem_data.jacobian_solver->name(),
     215             :                    "' and '",
     216           0 :                    lin_solver->name(),
     217             :                    "' are not referenced by another MFEM solver.");
     218        1094 :       problem_data.jacobian_solver = lin_solver;
     219             :     }
     220          56 :     else if (auto nonlinear_solver =
     221          56 :                  std::dynamic_pointer_cast<Moose::MFEM::NonlinearSolverBase>(solver);
     222          56 :              nonlinear_solver)
     223             :     {
     224          56 :       if (problem_data.nonlinear_solver)
     225           0 :         mooseError("Multiple MFEM nonlinear solver drivers provided. '",
     226           0 :                    problem_data.nonlinear_solver->name(),
     227             :                    "' and '",
     228           0 :                    nonlinear_solver->name(),
     229             :                    "' are not referenced by another MFEM solver.");
     230          56 :       problem_data.nonlinear_solver = nonlinear_solver;
     231             :     }
     232             :     else
     233           0 :       mooseError("Unsupported MFEM solver object type '",
     234           0 :                  solver->type(),
     235             :                  "' for solver '",
     236           0 :                  solver->name(),
     237        1206 :                  "'.");
     238        2160 :   }
     239             : 
     240        1101 :   _mfem_solver_definitions.clear();
     241        1101 : }
     242             : 
     243             : void
     244        1490 : MFEMProblem::addBoundaryCondition(const std::string & bc_name,
     245             :                                   const std::string & name,
     246             :                                   InputParameters & parameters)
     247             : {
     248        2980 :   auto bc = addObject<MFEMBoundaryCondition>(bc_name, name, parameters).front();
     249        1490 :   const auto & mfem_bc = *bc;
     250             : 
     251        1490 :   if (dynamic_cast<const MFEMIntegratedBC *>(&mfem_bc))
     252             :   {
     253          62 :     auto integrated_bc = std::dynamic_pointer_cast<MFEMIntegratedBC>(bc);
     254             :     auto eqsys =
     255          62 :         std::dynamic_pointer_cast<Moose::MFEM::EquationSystem>(getProblemData().eqn_system);
     256          62 :     if (eqsys)
     257          62 :       eqsys->AddIntegratedBC(std::move(integrated_bc));
     258             :     else
     259           0 :       mooseError("Cannot add integrated BC with name '" + name +
     260             :                  "' because there is no corresponding equation system.");
     261          62 :   }
     262        1428 :   else if (dynamic_cast<const MFEMComplexIntegratedBC *>(&mfem_bc))
     263             :   {
     264          14 :     auto integrated_bc = std::dynamic_pointer_cast<MFEMComplexIntegratedBC>(bc);
     265             :     auto eqsys =
     266          14 :         std::dynamic_pointer_cast<Moose::MFEM::ComplexEquationSystem>(getProblemData().eqn_system);
     267          14 :     if (eqsys)
     268          14 :       eqsys->AddComplexIntegratedBC(std::move(integrated_bc));
     269             :     else
     270           0 :       mooseError("Cannot add complex integrated BC with name '" + name +
     271             :                  "' because there is no corresponding equation system.");
     272          14 :   }
     273        1414 :   else if (dynamic_cast<const MFEMComplexEssentialBC *>(&mfem_bc))
     274             :   {
     275          63 :     auto essential_bc = std::dynamic_pointer_cast<MFEMComplexEssentialBC>(bc);
     276             :     auto eqsys =
     277          63 :         std::dynamic_pointer_cast<Moose::MFEM::ComplexEquationSystem>(getProblemData().eqn_system);
     278          63 :     if (eqsys)
     279          63 :       eqsys->AddComplexEssentialBCs(std::move(essential_bc));
     280             :     else
     281           0 :       mooseError("Cannot add boundary condition with name '" + name +
     282             :                  "' because there is no corresponding equation system.");
     283          63 :   }
     284        1351 :   else if (dynamic_cast<const MFEMEssentialBC *>(&mfem_bc))
     285             :   {
     286        1351 :     auto essential_bc = std::dynamic_pointer_cast<MFEMEssentialBC>(bc);
     287             :     auto eqsys =
     288        1351 :         std::dynamic_pointer_cast<Moose::MFEM::EquationSystem>(getProblemData().eqn_system);
     289        1351 :     if (eqsys)
     290        1351 :       eqsys->AddEssentialBC(std::move(essential_bc));
     291             :     else
     292           0 :       mooseError("Cannot add boundary condition with name '" + name +
     293             :                  "' because there is no corresponding equation system.");
     294        1351 :   }
     295             :   else
     296             :   {
     297           0 :     mooseError("Unsupported bc of type '", bc_name, "' and name '", name, "' detected.");
     298             :   }
     299        1490 : }
     300             : 
     301             : void
     302           0 : MFEMProblem::addMaterial(const std::string &, const std::string &, InputParameters &)
     303             : {
     304           0 :   mooseError(
     305             :       "MFEM materials must be added through the 'FunctorMaterials' block and not 'Materials'");
     306             : }
     307             : 
     308             : void
     309         378 : MFEMProblem::addFunctorMaterial(const std::string & material_name,
     310             :                                 const std::string & name,
     311             :                                 InputParameters & parameters)
     312             : {
     313         772 :   addObject<MFEMFunctorMaterial>(material_name, name, parameters);
     314         362 : }
     315             : 
     316             : void
     317        3009 : MFEMProblem::addFESpace(const std::string & type,
     318             :                         const std::string & name,
     319             :                         InputParameters & parameters)
     320             : {
     321        3009 :   if (getProblemData().fespace_hierarchies.Has(name))
     322           0 :     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        6018 :   auto & mfem_fespace = *addObject<MFEMFESpace>(type, name, parameters).front();
     328             : 
     329             :   // Register fespace and associated fe collection.
     330        3009 :   getProblemData().fecs.Register(name, mfem_fespace.getFEC());
     331        3009 :   getProblemData().fespaces.Register(name, mfem_fespace.getFESpace());
     332        3009 : }
     333             : 
     334             : void
     335          11 : MFEMProblem::addFESpaceHierarchy(const std::string & type,
     336             :                                  const std::string & name,
     337             :                                  InputParameters & parameters)
     338             : {
     339          11 :   if (getProblemData().fespaces.Has(name))
     340           0 :     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          22 :   auto hierarchy_obj = addObject<MFEMFESpaceHierarchy>(type, name, parameters).front();
     346          11 :   auto hierarchy_shared = hierarchy_obj->getHierarchyShared();
     347             :   // Register the hierarchy for co-ownership by solvers.
     348          11 :   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          11 :       hierarchy_shared, &hierarchy_obj->getHierarchy().GetFinestFESpace());
     354          11 :   getProblemData().fespaces.Register(name, finest);
     355          11 : }
     356             : 
     357             : void
     358        2117 : MFEMProblem::validateVariableNumericType(const std::string & var_type,
     359             :                                          const std::string & var_name) const
     360             : {
     361        2117 :   const bool variable_is_complex = var_type == "MFEMComplexVariable";
     362        2117 :   const bool problem_is_complex = _num_type == NumericType::COMPLEX;
     363        2117 :   if (variable_is_complex != problem_is_complex)
     364           8 :     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        2113 : }
     370             : 
     371             : void
     372        2091 : MFEMProblem::addVariable(const std::string & var_type,
     373             :                          const std::string & var_name,
     374             :                          InputParameters & parameters)
     375             : {
     376        2091 :   validateVariableNumericType(var_type, var_name);
     377        2087 :   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        2087 :   if (isTransient())
     382             :   {
     383             :     const auto time_derivative_var_name =
     384         299 :         getMFEMObject<MFEMVariable>("MooseVariableBase", var_name).getTimeDerivativeName();
     385         299 :     getProblemData().time_derivative_map.addTimeDerivativeAssociation(var_name,
     386             :                                                                       time_derivative_var_name);
     387         299 :     addGridFunction(var_type, time_derivative_var_name, parameters);
     388         299 :   }
     389        2087 : }
     390             : 
     391             : void
     392        4208 : MFEMProblem::addGridFunction(const std::string & var_type,
     393             :                              const std::string & var_name,
     394             :                              InputParameters & parameters)
     395             : {
     396             : 
     397        4208 :   if (var_type == "MFEMVariable" || var_type == "MFEMComplexVariable")
     398             :   {
     399             :     // Add MFEM variable directly.
     400        3991 :     if (var_type == "MFEMComplexVariable")
     401         231 :       addObject<MFEMComplexVariable>(var_type, var_name, parameters);
     402             :     else
     403       11742 :       addObject<MFEMVariable>(var_type, var_name, parameters);
     404             :   }
     405             :   else
     406             :   {
     407             :     // Add MOOSE variable.
     408         217 :     ExternalProblem::addVariable(var_type, var_name, parameters);
     409             : 
     410             :     // Add MFEM variable indirectly ("gridfunction").
     411         217 :     InputParameters mfem_variable_params = addMFEMFESpaceFromMOOSEVariable(parameters);
     412         868 :     addObject<MFEMVariable>("MFEMVariable", var_name, mfem_variable_params);
     413         217 :   }
     414             : 
     415             :   // Register gridfunction.
     416        4208 :   if (var_type == "MFEMComplexVariable")
     417             :   {
     418             :     MFEMComplexVariable & mfem_variable =
     419          77 :         getMFEMObject<MFEMComplexVariable>("MooseVariableBase", var_name);
     420          77 :     getProblemData().cmplx_gridfunctions.Register(var_name, mfem_variable.getComplexGridFunction());
     421          77 :     mfem_variable.declareCoefficients();
     422             :   }
     423             :   else // must be real, but may have been set up indirectly from a MOOSE variable
     424             :   {
     425        4131 :     MFEMVariable & mfem_variable = getMFEMObject<MFEMVariable>("MooseVariableBase", var_name);
     426        4131 :     getProblemData().gridfunctions.Register(var_name, mfem_variable.getGridFunction());
     427        4131 :     mfem_variable.declareCoefficients();
     428             :   }
     429        4208 : }
     430             : 
     431             : void
     432        1666 : 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        1666 :   addGridFunction(var_type, var_name, parameters);
     439        1666 : }
     440             : 
     441             : void
     442         542 : MFEMProblem::addAuxKernel(const std::string & kernel_name,
     443             :                           const std::string & name,
     444             :                           InputParameters & parameters)
     445             : {
     446        1084 :   addObject<MFEMExecutedObject>(kernel_name, name, parameters);
     447         542 : }
     448             : 
     449             : void
     450        2020 : MFEMProblem::addKernel(const std::string & kernel_name,
     451             :                        const std::string & name,
     452             :                        InputParameters & parameters)
     453             : {
     454        4040 :   auto kernel = addObject<MFEMKernel>(kernel_name, name, parameters).front();
     455        2020 :   const auto & kernel_object = *kernel;
     456             : 
     457        2020 :   if (dynamic_cast<const MFEMComplexKernel *>(&kernel_object))
     458             :   {
     459          63 :     auto complex_kernel = std::dynamic_pointer_cast<MFEMComplexKernel>(kernel);
     460             :     auto eqsys =
     461          63 :         std::dynamic_pointer_cast<Moose::MFEM::ComplexEquationSystem>(getProblemData().eqn_system);
     462          63 :     if (eqsys)
     463          63 :       eqsys->AddComplexKernel(std::move(complex_kernel));
     464             :     else
     465           0 :       mooseError("Cannot add complex kernel with name '" + name +
     466             :                  "' because there is no corresponding equation system.");
     467          63 :   }
     468             :   else
     469             :   {
     470             :     auto eqsys =
     471        1957 :         std::dynamic_pointer_cast<Moose::MFEM::EquationSystem>(getProblemData().eqn_system);
     472        1957 :     if (eqsys)
     473        1957 :       eqsys->AddKernel(std::move(kernel));
     474             :     else
     475           0 :       mooseError("Cannot add kernel with name '" + name +
     476             :                  "' because there is no corresponding equation system.");
     477        1957 :   }
     478        2020 : }
     479             : 
     480             : void
     481          63 : 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          63 :       getMFEMObject<MFEMComplexKernel>("Kernel", name).getSharedPtr());
     487         252 :   parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
     488         126 :   auto kernel_ptr = addObject<MFEMKernel>(kernel_name, name + "_real", parameters).front();
     489          63 :   parent_ptr->setRealKernel(kernel_ptr);
     490          63 : }
     491             : 
     492             : void
     493          56 : 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          56 :       getMFEMObject<MFEMComplexKernel>("Kernel", name).getSharedPtr());
     499         224 :   parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
     500         112 :   auto kernel_ptr = addObject<MFEMKernel>(kernel_name, name + "_imag", parameters).front();
     501          56 :   parent_ptr->setImagKernel(kernel_ptr);
     502          56 : }
     503             : 
     504             : void
     505           0 : MFEMProblem::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           0 :       getMFEMObject<MFEMComplexIntegratedBC>("BoundaryCondition", name).getSharedPtr());
     511           0 :   parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
     512           0 :   parameters.set<std::vector<BoundaryName>>("boundary") =
     513           0 :       parent_ptr->getParam<std::vector<BoundaryName>>("boundary");
     514             :   auto bc_ptr = std::dynamic_pointer_cast<MFEMIntegratedBC>(
     515           0 :       addObject<MFEMBoundaryCondition>(kernel_name, name + "_real", parameters).front());
     516           0 :   parent_ptr->setRealBC(bc_ptr);
     517           0 : }
     518             : 
     519             : void
     520           0 : MFEMProblem::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           0 :       getMFEMObject<MFEMComplexIntegratedBC>("BoundaryCondition", name).getSharedPtr());
     526           0 :   parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
     527           0 :   parameters.set<std::vector<BoundaryName>>("boundary") =
     528           0 :       parent_ptr->getParam<std::vector<BoundaryName>>("boundary");
     529             :   auto bc_ptr = std::dynamic_pointer_cast<MFEMIntegratedBC>(
     530           0 :       addObject<MFEMBoundaryCondition>(kernel_name, name + "_imag", parameters).front());
     531           0 :   parent_ptr->setImagBC(bc_ptr);
     532           0 : }
     533             : 
     534             : int
     535         396 : vectorFunctionDim(const std::string & type, const InputParameters & parameters)
     536             : {
     537         792 :   if (parameters.isParamSetByUser("expression_z"))
     538         366 :     return 3;
     539          90 :   if (parameters.isParamSetByUser("expression_y") || type == "LevelSetOlssonVortex")
     540          28 :     return 2;
     541           4 :   if (parameters.isParamSetByUser("expression_x"))
     542           2 :     return 1;
     543             : 
     544           0 :   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        1697 : MFEMProblem::addFunction(const std::string & type,
     588             :                          const std::string & name,
     589             :                          InputParameters & parameters)
     590             : {
     591        1697 :   ExternalProblem::addFunction(type, name, parameters);
     592        1697 :   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        1697 :   if (std::find(SCALAR_FUNCS.begin(), SCALAR_FUNCS.end(), type) != SCALAR_FUNCS.end())
     596             :   {
     597        1144 :     getCoefficients().declareScalar<mfem::FunctionCoefficient>(
     598             :         name,
     599        1144 :         [&func](const mfem::Vector & p, mfem::real_t t) -> mfem::real_t
     600     2327744 :         { return func.value(t, Moose::MFEM::libMeshPointFromMFEMVector(p)); });
     601             :   }
     602         553 :   else if (std::find(VECTOR_FUNCS.begin(), VECTOR_FUNCS.end(), type) != VECTOR_FUNCS.end())
     603             :   {
     604         396 :     int dim = vectorFunctionDim(type, parameters);
     605         396 :     getCoefficients().declareVector<mfem::VectorFunctionCoefficient>(
     606             :         name,
     607             :         dim,
     608         396 :         [&func, dim](const mfem::Vector & p, mfem::real_t t, mfem::Vector & u)
     609             :         {
     610             :           libMesh::RealVectorValue vector_value =
     611     2875414 :               func.vectorValue(t, Moose::MFEM::libMeshPointFromMFEMVector(p));
     612    11183410 :           for (int i = 0; i < dim; i++)
     613             :           {
     614     8307996 :             u[i] = vector_value(i);
     615             :           }
     616     2875414 :         });
     617             :   }
     618         157 :   else if ("MFEMParsedFunction" != type)
     619             :   {
     620           2 :     mooseWarning("Could not identify whether function ",
     621             :                  type,
     622             :                  " is scalar or vector; no MFEM coefficient object created.");
     623             :   }
     624        1695 : }
     625             : 
     626             : void
     627         904 : MFEMProblem::addPostprocessor(const std::string & type,
     628             :                               const std::string & name,
     629             :                               InputParameters & parameters)
     630             : {
     631         904 :   if (parameters.getSystemAttributeName() == "MFEMExecutedObject")
     632             :   {
     633        1600 :     checkUserObjectNameCollision(name, "Postprocessor");
     634        1600 :     addObject<MFEMExecutedObject>(type, name, parameters);
     635         800 :     const PostprocessorValue & val = getPostprocessorValueByName(name);
     636         800 :     getCoefficients().declareScalar<mfem::FunctionCoefficient>(
     637         802 :         name, [&val](const mfem::Vector &) -> mfem::real_t { return val; });
     638             :   }
     639             :   else
     640         104 :     ExternalProblem::addPostprocessor(type, name, parameters);
     641         904 : }
     642             : 
     643             : void
     644         452 : MFEMProblem::addVectorPostprocessor(const std::string & type,
     645             :                                     const std::string & name,
     646             :                                     InputParameters & parameters)
     647             : {
     648         452 :   if (parameters.getSystemAttributeName() == "MFEMExecutedObject")
     649             :   {
     650         904 :     checkUserObjectNameCollision(name, "VectorPostprocessor");
     651        1354 :     addObject<MFEMExecutedObject>(type, name, parameters);
     652             :   }
     653             :   else
     654           0 :     ExternalProblem::addVectorPostprocessor(type, name, parameters);
     655         450 : }
     656             : 
     657             : InputParameters
     658         217 : MFEMProblem::addMFEMFESpaceFromMOOSEVariable(InputParameters & parameters)
     659             : {
     660             : 
     661         434 :   InputParameters fespace_params = _factory.getValidParams("MFEMGenericFESpace");
     662         434 :   InputParameters variable_params = _factory.getValidParams("MFEMVariable");
     663             : 
     664         217 :   const auto family = Utility::string_to_enum<FEFamily>(parameters.get<MooseEnum>("family"));
     665         217 :   auto order = static_cast<int>(parameters.get<MooseEnum>("order"));
     666         217 :   const auto dim = mesh().dimension();
     667             : 
     668         217 :   std::string space;
     669         217 :   int vdim = 1;
     670             : 
     671         217 :   switch (family)
     672             :   {
     673          35 :     case FEFamily::LAGRANGE:
     674          35 :       space = "H1";
     675          35 :       break;
     676          35 :     case FEFamily::NEDELEC_ONE:
     677          35 :       space = "ND";
     678          35 :       break;
     679          35 :     case FEFamily::RAVIART_THOMAS:
     680          35 :       space = "RT";
     681          35 :       --order;
     682          35 :       break;
     683          42 :     case FEFamily::MONOMIAL:
     684             :     case FEFamily::L2_LAGRANGE:
     685          42 :       space = "L2";
     686          42 :       break;
     687          28 :     case FEFamily::LAGRANGE_VEC:
     688          28 :       space = "H1";
     689          28 :       vdim = dim;
     690          28 :       break;
     691          42 :     case FEFamily::MONOMIAL_VEC:
     692             :     case FEFamily::L2_LAGRANGE_VEC:
     693          42 :       space = "L2";
     694          42 :       vdim = dim;
     695          42 :       break;
     696           0 :     default:
     697           0 :       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         217 :   const auto fec_name = space + "_" + std::to_string(dim) + "D_P" + std::to_string(order);
     705         217 :   const auto fes_name = fec_name + "_X" + std::to_string(vdim);
     706             : 
     707             :   // Set all fespace parameters.
     708         217 :   fespace_params.set<std::string>("fec_name") = fec_name;
     709         651 :   fespace_params.set<int>("vdim") = vdim;
     710             : 
     711         434 :   if (!hasMFEMObject("MFEMFESpace", fes_name))
     712         266 :     addFESpace("MFEMGenericFESpace", fes_name, fespace_params);
     713             : 
     714         651 :   variable_params.set<MFEMFESpaceName>("fespace") = fes_name;
     715             : 
     716         434 :   return variable_params;
     717         217 : }
     718             : 
     719             : void
     720        3272 : MFEMProblem::displaceMesh()
     721             : {
     722             :   // Displace mesh
     723        3272 :   if (mesh().shouldDisplace())
     724             :   {
     725          38 :     mesh().displace(static_cast<mfem::GridFunction const &>(*getMeshDisplacementGridFunction()));
     726             :     // TODO: update FESpaces GridFunctions etc for transient solves
     727             :   }
     728        3272 : }
     729             : 
     730             : std::optional<std::reference_wrapper<mfem::ParGridFunction const>>
     731        1732 : MFEMProblem::getMeshDisplacementGridFunction()
     732             : {
     733             :   // If C++23 transform were available this would be easier
     734        1732 :   auto const displacement_variable = mesh().getMeshDisplacementVariable();
     735        1732 :   if (displacement_variable)
     736             :   {
     737          70 :     return *_problem_data.gridfunctions.Get(displacement_variable.value());
     738             :   }
     739             :   else
     740             :   {
     741        1662 :     return std::nullopt;
     742             :   }
     743             : }
     744             : 
     745             : void
     746           0 : MFEMProblem::rebalanceMesh(mfem::ParMesh & pmesh)
     747             : {
     748           0 :   if (pmesh.Nonconforming())
     749             :   {
     750           0 :     pmesh.Rebalance();
     751           0 :     updateFESpaces();
     752           0 :     updateGridFunctions();
     753             :   }
     754           0 : }
     755             : 
     756             : void
     757          13 : MFEMProblem::updateFESpaces()
     758             : {
     759          39 :   for (const auto & fe_space_pair : _problem_data.fespaces)
     760          26 :     fe_space_pair.second->Update();
     761          13 : }
     762             : 
     763             : void
     764          26 : MFEMProblem::updateGridFunctions()
     765             : {
     766          78 :   for (const auto & gridfunction_pair : _problem_data.gridfunctions)
     767          52 :     gridfunction_pair.second->Update();
     768          26 : }
     769             : 
     770             : std::vector<VariableName>
     771           0 : MFEMProblem::getAuxVariableNames()
     772             : {
     773           0 :   return systemBaseAuxiliary().getVariableNames();
     774             : }
     775             : 
     776             : MFEMMesh &
     777       75549 : MFEMProblem::mesh()
     778             : {
     779             :   mooseAssert(ExternalProblem::mesh().type() == "MFEMMesh",
     780             :               "Please choose the MFEMMesh mesh type for an MFEMProblem\n");
     781       75549 :   return static_cast<MFEMMesh &>(_mesh);
     782             : }
     783             : 
     784             : const MFEMMesh &
     785        3693 : MFEMProblem::mesh() const
     786             : {
     787        3693 :   return const_cast<MFEMProblem *>(this)->mesh();
     788             : }
     789             : 
     790             : void
     791         206 : MFEMProblem::addSubMesh(const std::string & var_type,
     792             :                         const std::string & var_name,
     793             :                         InputParameters & parameters)
     794             : {
     795         412 :   auto & mfem_submesh = *addObject<MFEMSubMesh>(var_type, var_name, parameters).front();
     796             :   // Register submesh.
     797         206 :   getProblemData().submeshes.Register(var_name, mfem_submesh.getSubMesh());
     798         206 : }
     799             : 
     800             : void
     801          36 : 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          72 :   addObject<MFEMObject>(type, name, parameters);
     807          36 : }
     808             : 
     809             : void
     810         688 : MFEMProblem::addTransfer(const std::string & transfer_name,
     811             :                          const std::string & name,
     812             :                          InputParameters & parameters)
     813             : {
     814         688 :   if (parameters.getBase() == "MFEMSubMeshTransfer")
     815         747 :     addObject<MFEMExecutedObject>(transfer_name, name, parameters);
     816             :   else
     817         439 :     ExternalProblem::addTransfer(transfer_name, name, parameters);
     818         688 : }
     819             : 
     820             : void
     821        1272 : MFEMProblem::addInitialCondition(const std::string & ic_name,
     822             :                                  const std::string & name,
     823             :                                  InputParameters & parameters)
     824             : {
     825        2544 :   addObject<MFEMExecutedObject>(ic_name, name, parameters);
     826        1272 : }
     827             : 
     828             : void
     829       11756 : MFEMProblem::executeMFEMObjects(const ExecFlagType & exec_type)
     830             : {
     831       11756 :   std::vector<MFEMExecutedObject *> objects;
     832       11756 :   theWarehouse()
     833       11756 :       .query()
     834       11756 :       .condition<AttribSystem>("MFEMExecutedObject")
     835       11756 :       .condition<AttribExecOns>(exec_type)
     836       23512 :       .condition<AttribThread>(0)
     837       11756 :       .queryInto(objects);
     838             : 
     839       11756 :   std::map<std::string, const MFEMExecutedObject *> suppliers;
     840       16382 :   for (auto * const object : objects)
     841        9254 :     for (const auto & item : object->getSuppliedItems())
     842             :     {
     843        4628 :       const auto [it, inserted] = suppliers.emplace(item, object);
     844        4628 :       if (!inserted && it->second != object)
     845           2 :         mooseError("MFEM executed-object dependency ambiguity on ",
     846             :                    exec_type,
     847             :                    ": both '",
     848           2 :                    it->second->name(),
     849             :                    "' and '",
     850           2 :                    object->name(),
     851             :                    "' supply '",
     852             :                    item,
     853             :                    "'.");
     854             :     }
     855             : 
     856       16378 :   for (auto * const object : objects)
     857             :   {
     858        4624 :     object->initialize();
     859        4624 :     object->execute();
     860        4624 :     object->finalize();
     861             : 
     862        4624 :     if (auto * const pp = dynamic_cast<const Postprocessor *>(object))
     863             :     {
     864        1328 :       _reporter_data.finalize(pp->PPName());
     865        1328 :       setPostprocessorValueByName(pp->PPName(), pp->getValue());
     866             :     }
     867             : 
     868        4624 :     if (auto * const vpp = dynamic_cast<VectorPostprocessor *>(object))
     869        1096 :       _reporter_data.finalize(vpp->PPName());
     870             :   }
     871       11758 : }
     872             : 
     873             : std::string
     874        1676 : 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        1676 :   std::vector<std::string> solvers;
     879             : 
     880        1676 :   if (getProblemData().nonlinear_solver)
     881          56 :     solvers.push_back(MooseUtils::prettyCppType(getProblemData().nonlinear_solver.get()));
     882             : 
     883        1676 :   if (getProblemData().jacobian_solver)
     884             :   {
     885        1094 :     solvers.push_back(MooseUtils::prettyCppType(getProblemData().jacobian_solver.get()));
     886        1094 :     if (const auto * prec = getProblemData().jacobian_solver->GetPreconditioner())
     887         977 :       solvers.push_back(MooseUtils::prettyCppType(prec));
     888             :   }
     889             : 
     890        6129 :   return solvers.empty() ? "None" : MooseUtils::stringJoin(solvers);
     891        1676 : }
     892             : 
     893             : bool
     894         217 : MFEMProblem::hasMFEMObject(const std::string & system, const std::string & name) const
     895             : {
     896         217 :   std::vector<MooseObject *> objs;
     897         217 :   theWarehouse()
     898         217 :       .query()
     899         217 :       .condition<AttribSystem>(system)
     900         434 :       .condition<AttribThread>(0)
     901         217 :       .condition<AttribName>(name)
     902         217 :       .queryInto(objs);
     903         434 :   return !objs.empty();
     904         217 : }
     905             : 
     906             : #endif

Generated by: LCOV version 1.14