LCOV - code coverage report
Current view: top level - src/mfem/problem - MFEMProblem.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 199 214 93.0 %
Date: 2025-07-17 01:28:37 Functions: 33 35 94.3 %
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 MFEM_ENABLED
      11             : 
      12             : #include "MFEMProblem.h"
      13             : #include "MFEMInitialCondition.h"
      14             : #include "MFEMVariable.h"
      15             : #include "MFEMSubMesh.h"
      16             : #include "MFEMFunctorMaterial.h"
      17             : #include "libmesh/string_to_enum.h"
      18             : 
      19             : #include <vector>
      20             : #include <algorithm>
      21             : 
      22             : registerMooseObject("MooseApp", MFEMProblem);
      23             : 
      24             : InputParameters
      25        9158 : MFEMProblem::validParams()
      26             : {
      27        9158 :   InputParameters params = ExternalProblem::validParams();
      28        9158 :   params.addClassDescription("Problem type for building and solving finite element problem using"
      29             :                              " the MFEM finite element library.");
      30        9158 :   return params;
      31           0 : }
      32             : 
      33         264 : MFEMProblem::MFEMProblem(const InputParameters & params) : ExternalProblem(params)
      34             : {
      35             :   // Initialise Hypre for all MFEM problems.
      36         264 :   mfem::Hypre::Init();
      37         264 : }
      38             : 
      39             : void
      40         134 : MFEMProblem::initialSetup()
      41             : {
      42         134 :   FEProblemBase::initialSetup();
      43         134 :   addMFEMNonlinearSolver();
      44         134 : }
      45             : 
      46             : void
      47         134 : MFEMProblem::setMesh()
      48             : {
      49         134 :   auto pmesh = mesh().getMFEMParMeshPtr();
      50         134 :   getProblemData().pmesh = pmesh;
      51         134 :   getProblemData().comm = pmesh->GetComm();
      52         134 :   MPI_Comm_size(pmesh->GetComm(), &(getProblemData().num_procs));
      53         134 :   MPI_Comm_rank(pmesh->GetComm(), &(getProblemData().myid));
      54         134 : }
      55             : 
      56             : void
      57         134 : MFEMProblem::initProblemOperator()
      58             : {
      59         134 :   setMesh();
      60         134 :   auto mfem_exec_ptr = dynamic_cast<MFEMExecutioner *>(_app.getExecutioner());
      61         134 :   if (mfem_exec_ptr != nullptr)
      62             :   {
      63         134 :     mfem_exec_ptr->constructProblemOperator();
      64             :   }
      65             :   else
      66             :   {
      67           0 :     mooseError("Executioner used that is not currently supported by MFEMProblem");
      68             :   }
      69         134 : }
      70             : 
      71             : void
      72         136 : MFEMProblem::addMFEMPreconditioner(const std::string & user_object_name,
      73             :                                    const std::string & name,
      74             :                                    InputParameters & parameters)
      75             : {
      76         136 :   FEProblemBase::addUserObject(user_object_name, name, parameters);
      77         136 : }
      78             : 
      79             : void
      80         102 : MFEMProblem::addMFEMSolver(const std::string & user_object_name,
      81             :                            const std::string & name,
      82             :                            InputParameters & parameters)
      83             : {
      84         102 :   FEProblemBase::addUserObject(user_object_name, name, parameters);
      85         102 :   auto object_ptr = getUserObject<MFEMSolverBase>(name).getSharedPtr();
      86             : 
      87         102 :   getProblemData().jacobian_solver = std::dynamic_pointer_cast<MFEMSolverBase>(object_ptr);
      88         102 : }
      89             : 
      90             : void
      91         134 : MFEMProblem::addMFEMNonlinearSolver()
      92             : {
      93         134 :   auto nl_solver = std::make_shared<mfem::NewtonSolver>(getProblemData().comm);
      94             : 
      95             :   // Defaults to one iteration, without further nonlinear iterations
      96         134 :   nl_solver->SetRelTol(0.0);
      97         134 :   nl_solver->SetAbsTol(0.0);
      98         134 :   nl_solver->SetMaxIter(1);
      99             : 
     100         134 :   getProblemData().nonlinear_solver = nl_solver;
     101         134 : }
     102             : 
     103             : void
     104         170 : MFEMProblem::addBoundaryCondition(const std::string & bc_name,
     105             :                                   const std::string & name,
     106             :                                   InputParameters & parameters)
     107             : {
     108         170 :   FEProblemBase::addUserObject(bc_name, name, parameters);
     109         170 :   const UserObject * mfem_bc_uo = &(getUserObjectBase(name));
     110         170 :   if (dynamic_cast<const MFEMIntegratedBC *>(mfem_bc_uo) != nullptr)
     111             :   {
     112           8 :     auto object_ptr = getUserObject<MFEMIntegratedBC>(name).getSharedPtr();
     113           8 :     auto bc = std::dynamic_pointer_cast<MFEMIntegratedBC>(object_ptr);
     114           8 :     if (getProblemData().eqn_system)
     115             :     {
     116           8 :       getProblemData().eqn_system->AddIntegratedBC(std::move(bc));
     117             :     }
     118             :     else
     119             :     {
     120           0 :       mooseError("Cannot add integrated BC with name '" + name +
     121             :                  "' because there is no corresponding equation system.");
     122             :     }
     123           8 :   }
     124         162 :   else if (dynamic_cast<const MFEMEssentialBC *>(mfem_bc_uo) != nullptr)
     125             :   {
     126         162 :     auto object_ptr = getUserObject<MFEMEssentialBC>(name).getSharedPtr();
     127         162 :     auto mfem_bc = std::dynamic_pointer_cast<MFEMEssentialBC>(object_ptr);
     128         162 :     if (getProblemData().eqn_system)
     129             :     {
     130         162 :       getProblemData().eqn_system->AddEssentialBC(std::move(mfem_bc));
     131             :     }
     132             :     else
     133             :     {
     134           0 :       mooseError("Cannot add boundary condition with name '" + name +
     135             :                  "' because there is no corresponding equation system.");
     136             :     }
     137         162 :   }
     138             :   else
     139             :   {
     140           0 :     mooseError("Unsupported bc of type '", bc_name, "' and name '", name, "' detected.");
     141             :   }
     142         170 : }
     143             : 
     144             : void
     145           0 : MFEMProblem::addMaterial(const std::string &, const std::string &, InputParameters &)
     146             : {
     147           0 :   mooseError(
     148             :       "MFEM materials must be added through the 'FunctorMaterials' block and not 'Materials'");
     149             : }
     150             : 
     151             : void
     152          57 : MFEMProblem::addFunctorMaterial(const std::string & material_name,
     153             :                                 const std::string & name,
     154             :                                 InputParameters & parameters)
     155             : {
     156          57 :   FEProblemBase::addUserObject(material_name, name, parameters);
     157          49 :   getUserObject<MFEMFunctorMaterial>(name);
     158          49 : }
     159             : 
     160             : void
     161         212 : MFEMProblem::addFESpace(const std::string & user_object_name,
     162             :                         const std::string & name,
     163             :                         InputParameters & parameters)
     164             : {
     165         212 :   FEProblemBase::addUserObject(user_object_name, name, parameters);
     166         212 :   MFEMFESpace & mfem_fespace(getUserObject<MFEMFESpace>(name));
     167             : 
     168             :   // Register fespace and associated fe collection.
     169         212 :   getProblemData().fecs.Register(name, mfem_fespace.getFEC());
     170         212 :   getProblemData().fespaces.Register(name, mfem_fespace.getFESpace());
     171         212 : }
     172             : 
     173             : void
     174         210 : MFEMProblem::addVariable(const std::string & var_type,
     175             :                          const std::string & var_name,
     176             :                          InputParameters & parameters)
     177             : {
     178         210 :   addGridFunction(var_type, var_name, parameters);
     179             :   // MOOSE variables store DoFs for the trial variable and its time derivatives up to second order;
     180             :   // MFEM GridFunctions store data for only one set of DoFs each, so we must add additional
     181             :   // GridFunctions for time derivatives.
     182         210 :   if (isTransient())
     183             :   {
     184          40 :     addGridFunction(var_type, Moose::MFEM::GetTimeDerivativeName(var_name), parameters);
     185             :   }
     186         210 : }
     187             : 
     188             : void
     189         250 : MFEMProblem::addGridFunction(const std::string & var_type,
     190             :                              const std::string & var_name,
     191             :                              InputParameters & parameters)
     192             : {
     193         250 :   if (var_type == "MFEMVariable")
     194             :   {
     195             :     // Add MFEM variable directly.
     196         218 :     FEProblemBase::addUserObject(var_type, var_name, parameters);
     197             :   }
     198             :   else
     199             :   {
     200             :     // Add MOOSE variable.
     201          32 :     FEProblemBase::addVariable(var_type, var_name, parameters);
     202             : 
     203             :     // Add MFEM variable indirectly ("gridfunction").
     204          32 :     InputParameters mfem_variable_params = addMFEMFESpaceFromMOOSEVariable(parameters);
     205          32 :     FEProblemBase::addUserObject("MFEMVariable", var_name, mfem_variable_params);
     206          32 :   }
     207             : 
     208             :   // Register gridfunction.
     209         250 :   MFEMVariable & mfem_variable = getUserObject<MFEMVariable>(var_name);
     210         250 :   getProblemData().gridfunctions.Register(var_name, mfem_variable.getGridFunction());
     211         250 :   if (mfem_variable.getFESpace().isScalar())
     212         175 :     getCoefficients().declareScalar<mfem::GridFunctionCoefficient>(
     213         175 :         var_name, mfem_variable.getGridFunction().get());
     214             :   else
     215          75 :     getCoefficients().declareVector<mfem::VectorGridFunctionCoefficient>(
     216          75 :         var_name, mfem_variable.getGridFunction().get());
     217         250 : }
     218             : 
     219             : void
     220          74 : MFEMProblem::addAuxVariable(const std::string & var_type,
     221             :                             const std::string & var_name,
     222             :                             InputParameters & parameters)
     223             : {
     224             :   // We do not handle MFEM AuxVariables separately from variables currently
     225          74 :   addVariable(var_type, var_name, parameters);
     226          74 : }
     227             : 
     228             : void
     229          40 : MFEMProblem::addAuxKernel(const std::string & kernel_name,
     230             :                           const std::string & name,
     231             :                           InputParameters & parameters)
     232             : {
     233          40 :   FEProblemBase::addUserObject(kernel_name, name, parameters);
     234          40 : }
     235             : 
     236             : void
     237         162 : MFEMProblem::addKernel(const std::string & kernel_name,
     238             :                        const std::string & name,
     239             :                        InputParameters & parameters)
     240             : {
     241         162 :   FEProblemBase::addUserObject(kernel_name, name, parameters);
     242         162 :   const UserObject * kernel_uo = &(getUserObjectBase(name));
     243             : 
     244         162 :   if (dynamic_cast<const MFEMKernel *>(kernel_uo) != nullptr)
     245             :   {
     246         162 :     auto object_ptr = getUserObject<MFEMKernel>(name).getSharedPtr();
     247         162 :     auto kernel = std::dynamic_pointer_cast<MFEMKernel>(object_ptr);
     248         162 :     if (getProblemData().eqn_system)
     249             :     {
     250         162 :       getProblemData().eqn_system->AddKernel(std::move(kernel));
     251             :     }
     252             :     else
     253             :     {
     254           0 :       mooseError("Cannot add kernel with name '" + name +
     255             :                  "' because there is no corresponding equation system.");
     256             :     }
     257         162 :   }
     258             :   else
     259             :   {
     260           0 :     mooseError("Unsupported kernel of type '", kernel_name, "' and name '", name, "' detected.");
     261             :   }
     262         162 : }
     263             : 
     264             : libMesh::Point
     265     1039138 : pointFromMFEMVector(const mfem::Vector & vec)
     266             : {
     267             :   return libMesh::Point(
     268     1039138 :       vec.Elem(0), vec.Size() > 1 ? vec.Elem(1) : 0., vec.Size() > 2 ? vec.Elem(2) : 0.);
     269             : }
     270             : 
     271             : int
     272          64 : vectorFunctionDim(const std::string & type, const InputParameters & parameters)
     273             : {
     274          64 :   if (type == "LevelSetOlssonVortex")
     275             :   {
     276           0 :     return 2;
     277             :   }
     278          64 :   else if (type == "ParsedVectorFunction")
     279             :   {
     280          64 :     if (parameters.isParamSetByUser("expression_z") || parameters.isParamSetByUser("value_z"))
     281             :     {
     282          56 :       return 3;
     283             :     }
     284           8 :     else if (parameters.isParamSetByUser("expression_y") || parameters.isParamSetByUser("value_y"))
     285             :     {
     286           7 :       return 2;
     287             :     }
     288             :     else
     289             :     {
     290           1 :       return 1;
     291             :     }
     292             :   }
     293             :   else
     294             :   {
     295           0 :     return 3;
     296             :   }
     297             : }
     298             : 
     299             : const std::vector<std::string> SCALAR_FUNCS = {"Axisymmetric2D3DSolutionFunction",
     300             :                                                "BicubicSplineFunction",
     301             :                                                "CoarsenedPiecewiseLinear",
     302             :                                                "CompositeFunction",
     303             :                                                "ConstantFunction",
     304             :                                                "ImageFunction",
     305             :                                                "ParsedFunction",
     306             :                                                "ParsedGradFunction",
     307             :                                                "PeriodicFunction",
     308             :                                                "PiecewiseBilinear",
     309             :                                                "PiecewiseConstant",
     310             :                                                "PiecewiseConstantFromCSV",
     311             :                                                "PiecewiseLinear",
     312             :                                                "PiecewiseLinearFromVectorPostprocessor",
     313             :                                                "PiecewiseMultiInterpolation",
     314             :                                                "PiecewiseMulticonstant",
     315             :                                                "SolutionFunction",
     316             :                                                "SplineFunction",
     317             :                                                "FunctionSeries",
     318             :                                                "LevelSetOlssonBubble",
     319             :                                                "LevelSetOlssonPlane",
     320             :                                                "NearestReporterCoordinatesFunction",
     321             :                                                "ParameterMeshFunction",
     322             :                                                "ParsedOptimizationFunction",
     323             :                                                "FourierNoise",
     324             :                                                "MovingPlanarFront",
     325             :                                                "MultiControlDrumFunction",
     326             :                                                "Grad2ParsedFunction",
     327             :                                                "GradParsedFunction",
     328             :                                                "RichardsExcavGeom",
     329             :                                                "ScaledAbsDifferenceDRLRewardFunction",
     330             :                                                "CircularAreaHydraulicDiameterFunction",
     331             :                                                "CosineHumpFunction",
     332             :                                                "CosineTransitionFunction",
     333             :                                                "CubicTransitionFunction",
     334             :                                                "GeneralizedCircumference",
     335             :                                                "PiecewiseFunction",
     336             :                                                "TimeRampFunction"},
     337             :                                VECTOR_FUNCS = {"ParsedVectorFunction", "LevelSetOlssonVortex"};
     338             : 
     339             : void
     340         125 : MFEMProblem::addFunction(const std::string & type,
     341             :                          const std::string & name,
     342             :                          InputParameters & parameters)
     343             : {
     344         125 :   ExternalProblem::addFunction(type, name, parameters);
     345         125 :   auto & func = getFunction(name);
     346             :   // FIXME: Do we want to have optimised versions for when functions
     347             :   // are only of space or only of time.
     348         125 :   if (std::find(SCALAR_FUNCS.begin(), SCALAR_FUNCS.end(), type) != SCALAR_FUNCS.end())
     349             :   {
     350          60 :     getCoefficients().declareScalar<mfem::FunctionCoefficient>(
     351             :         name,
     352          60 :         [&func](const mfem::Vector & p, double t) -> mfem::real_t
     353      642099 :         { return func.value(t, pointFromMFEMVector(p)); });
     354             :   }
     355          65 :   else if (std::find(VECTOR_FUNCS.begin(), VECTOR_FUNCS.end(), type) != VECTOR_FUNCS.end())
     356             :   {
     357          64 :     int dim = vectorFunctionDim(type, parameters);
     358          64 :     getCoefficients().declareVector<mfem::VectorFunctionCoefficient>(
     359             :         name,
     360             :         dim,
     361          64 :         [&func, dim](const mfem::Vector & p, double t, mfem::Vector & u)
     362             :         {
     363      397039 :           libMesh::RealVectorValue vector_value = func.vectorValue(t, pointFromMFEMVector(p));
     364     1476889 :           for (int i = 0; i < dim; i++)
     365             :           {
     366     1079850 :             u[i] = vector_value(i);
     367             :           }
     368      397039 :         });
     369             :   }
     370             :   else
     371             :   {
     372           1 :     mooseWarning("Could not identify whether function ",
     373             :                  type,
     374             :                  " is scalar or vector; no MFEM coefficient object created.");
     375             :   }
     376         124 : }
     377             : 
     378             : void
     379          13 : MFEMProblem::addPostprocessor(const std::string & type,
     380             :                               const std::string & name,
     381             :                               InputParameters & parameters)
     382             : {
     383             :   // For some reason this isn't getting called
     384          13 :   ExternalProblem::addPostprocessor(type, name, parameters);
     385          13 :   const PostprocessorValue & val = getPostprocessorValueByName(name);
     386          13 :   getCoefficients().declareScalar<mfem::FunctionCoefficient>(
     387          14 :       name, [&val](const mfem::Vector &, double) -> mfem::real_t { return val; });
     388          13 : }
     389             : 
     390             : InputParameters
     391          32 : MFEMProblem::addMFEMFESpaceFromMOOSEVariable(InputParameters & parameters)
     392             : {
     393             : 
     394          32 :   InputParameters fespace_params = _factory.getValidParams("MFEMGenericFESpace");
     395          32 :   InputParameters mfem_variable_params = _factory.getValidParams("MFEMVariable");
     396             : 
     397             :   auto moose_fe_type =
     398          64 :       FEType(Utility::string_to_enum<Order>(parameters.get<MooseEnum>("order")),
     399          96 :              Utility::string_to_enum<FEFamily>(parameters.get<MooseEnum>("family")));
     400             : 
     401          32 :   std::string mfem_family;
     402          32 :   int mfem_vdim = 1;
     403             : 
     404          32 :   switch (moose_fe_type.family)
     405             :   {
     406           8 :     case FEFamily::LAGRANGE:
     407           8 :       mfem_family = "H1";
     408           8 :       mfem_vdim = 1;
     409           8 :       break;
     410           8 :     case FEFamily::LAGRANGE_VEC:
     411           8 :       mfem_family = "H1";
     412           8 :       mfem_vdim = 3;
     413           8 :       break;
     414           8 :     case FEFamily::MONOMIAL:
     415           8 :       mfem_family = "L2";
     416           8 :       mfem_vdim = 1;
     417           8 :       break;
     418           8 :     case FEFamily::MONOMIAL_VEC:
     419           8 :       mfem_family = "L2";
     420           8 :       mfem_vdim = 3;
     421           8 :       break;
     422           0 :     default:
     423           0 :       mooseError("Unable to set MFEM FESpace for MOOSE variable");
     424             :       break;
     425             :   }
     426             : 
     427             :   // Create fespace name. If this already exists, we will reuse this for
     428             :   // the mfem variable ("gridfunction").
     429          64 :   const std::string fespace_name = mfem_family + "_" +
     430         128 :                                    std::to_string(mesh().getMFEMParMesh().Dimension()) + "D_P" +
     431         128 :                                    std::to_string(moose_fe_type.order.get_order());
     432             : 
     433             :   // Set all fespace parameters.
     434          32 :   fespace_params.set<std::string>("fec_name") = fespace_name;
     435          32 :   fespace_params.set<int>("vdim") = mfem_vdim;
     436             : 
     437          32 :   if (!hasUserObject(fespace_name)) // Create the fespace (implicit).
     438             :   {
     439           8 :     addFESpace("MFEMGenericFESpace", fespace_name, fespace_params);
     440             :   }
     441             : 
     442          32 :   mfem_variable_params.set<UserObjectName>("fespace") = fespace_name;
     443             : 
     444          64 :   return mfem_variable_params;
     445          32 : }
     446             : 
     447             : void
     448         110 : MFEMProblem::displaceMesh()
     449             : {
     450             :   // Displace mesh
     451         110 :   if (mesh().shouldDisplace())
     452             :   {
     453           8 :     mesh().displace(static_cast<mfem::GridFunction const &>(*getMeshDisplacementGridFunction()));
     454             :     // TODO: update FESpaces GridFunctions etc for transient solves
     455             :   }
     456         110 : }
     457             : 
     458             : std::optional<std::reference_wrapper<mfem::ParGridFunction const>>
     459         142 : MFEMProblem::getMeshDisplacementGridFunction()
     460             : {
     461             :   // If C++23 transform were available this would be easier
     462         142 :   auto const displacement_variable = mesh().getMeshDisplacementVariable();
     463         142 :   if (displacement_variable)
     464             :   {
     465          16 :     return *_problem_data.gridfunctions.Get(displacement_variable.value());
     466             :   }
     467             :   else
     468             :   {
     469         126 :     return std::nullopt;
     470             :   }
     471             : }
     472             : 
     473             : std::vector<VariableName>
     474           0 : MFEMProblem::getAuxVariableNames()
     475             : {
     476           0 :   return systemBaseAuxiliary().getVariableNames();
     477             : }
     478             : 
     479             : MFEMMesh &
     480        3706 : MFEMProblem::mesh()
     481             : {
     482             :   mooseAssert(ExternalProblem::mesh().type() == "MFEMMesh",
     483             :               "Please choose the MFEMMesh mesh type for an MFEMProblem\n");
     484        3706 :   return static_cast<MFEMMesh &>(_mesh);
     485             : }
     486             : 
     487             : const MFEMMesh &
     488         506 : MFEMProblem::mesh() const
     489             : {
     490         506 :   return const_cast<MFEMProblem *>(this)->mesh();
     491             : }
     492             : 
     493             : void
     494          12 : MFEMProblem::addSubMesh(const std::string & var_type,
     495             :                         const std::string & var_name,
     496             :                         InputParameters & parameters)
     497             : {
     498             :   // Add MFEM SubMesh.
     499          12 :   FEProblemBase::addUserObject(var_type, var_name, parameters);
     500             :   // Register submesh.
     501          12 :   MFEMSubMesh & mfem_submesh = getUserObject<MFEMSubMesh>(var_name);
     502          12 :   getProblemData().submeshes.Register(var_name, mfem_submesh.getSubMesh());
     503          12 : }
     504             : 
     505             : void
     506          22 : MFEMProblem::addTransfer(const std::string & transfer_name,
     507             :                          const std::string & name,
     508             :                          InputParameters & parameters)
     509             : {
     510          22 :   if (parameters.get<std::string>("_moose_base") == "MFEMSubMeshTransfer")
     511           4 :     FEProblemBase::addUserObject(transfer_name, name, parameters);
     512             :   else
     513          18 :     FEProblemBase::addTransfer(transfer_name, name, parameters);
     514          22 : }
     515             : 
     516             : std::shared_ptr<mfem::ParGridFunction>
     517          50 : MFEMProblem::getGridFunction(const std::string & name)
     518             : {
     519          50 :   return getUserObject<MFEMVariable>(name).getGridFunction();
     520             : }
     521             : 
     522             : void
     523          50 : MFEMProblem::addInitialCondition(const std::string & ic_name,
     524             :                                  const std::string & name,
     525             :                                  InputParameters & parameters)
     526             : {
     527          50 :   FEProblemBase::addUserObject(ic_name, name, parameters);
     528          50 :   getUserObject<MFEMInitialCondition>(name); // error check
     529          50 : }
     530             : 
     531             : std::string
     532         134 : MFEMProblem::solverTypeString(const unsigned int libmesh_dbg_var(solver_sys_num))
     533             : {
     534             :   mooseAssert(solver_sys_num == 0, "No support for multi-system with MFEM right now");
     535         134 :   return MooseUtils::prettyCppType(getProblemData().jacobian_solver.get());
     536             : }
     537             : 
     538             : #endif

Generated by: LCOV version 1.14