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

Generated by: LCOV version 1.14