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

Generated by: LCOV version 1.14