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 "libmesh/string_to_enum.h"
20 :
21 : #include <vector>
22 : #include <algorithm>
23 : #include <map>
24 : #include <set>
25 : #include <deque>
26 : #include <sstream>
27 :
28 : registerMooseObject("MooseApp", MFEMProblem);
29 :
30 : InputParameters
31 7850 : MFEMProblem::validParams()
32 : {
33 7850 : InputParameters params = ExternalProblem::validParams();
34 15700 : params.addClassDescription("Problem type for building and solving the finite element problem "
35 : "using the MFEM finite element library.");
36 31400 : MooseEnum numeric_types("real complex", "real");
37 23550 : params.addParam<MooseEnum>("numeric_type", numeric_types, "Number type used for the problem");
38 :
39 15700 : return params;
40 7850 : }
41 :
42 1827 : MFEMProblem::MFEMProblem(const InputParameters & params)
43 3654 : : ExternalProblem(params), _num_type{static_cast<int>(getParam<MooseEnum>("numeric_type"))}
44 : {
45 : // Initialise Hypre for all MFEM problems.
46 1827 : mfem::Hypre::Init();
47 : // Disable multithreading for all MFEM problems (including any libMesh or MFEM subapps).
48 1827 : libMesh::libMeshPrivateData::_n_threads = 1;
49 : #ifdef LIBMESH_HAVE_OPENMP
50 1827 : omp_set_num_threads(1);
51 : #endif
52 1827 : setMesh();
53 1827 : }
54 :
55 : void
56 1479 : MFEMProblem::initialSetup()
57 : {
58 1479 : ExternalProblem::initialSetup();
59 :
60 : // MFEM indicators create their estimators during addIndicator(); markers still need an explicit
61 : // setup pass because they are no longer initialized through the libMesh/MOOSE user-object path.
62 1479 : std::vector<MFEMRefinementMarker *> markers;
63 1479 : theWarehouse().query().condition<AttribSystem>("Marker").queryInto(markers);
64 1505 : for (auto marker : markers)
65 26 : marker->initialSetup();
66 1479 : }
67 :
68 : void
69 9987 : MFEMProblem::execute(const ExecFlagType & exec_type)
70 : {
71 9987 : setCurrentExecuteOnFlag(exec_type);
72 9987 : executeMFEMObjects(exec_type);
73 :
74 9987 : ExternalProblem::execute(exec_type);
75 9987 : }
76 :
77 : void
78 1827 : MFEMProblem::setMesh()
79 : {
80 1827 : auto pmesh = mesh().getMFEMParMeshPtr();
81 1827 : getProblemData().pmesh = pmesh;
82 1827 : getProblemData().comm = pmesh->GetComm();
83 1827 : getProblemData().num_procs = pmesh->GetNRanks();
84 1827 : getProblemData().myid = pmesh->GetMyRank();
85 1827 : }
86 :
87 : void
88 1128 : MFEMProblem::addMFEMPreconditioner(const std::string & user_object_name,
89 : const std::string & name,
90 : InputParameters & parameters)
91 : {
92 2256 : addObject<Moose::MFEM::SolverBase>(user_object_name, name, parameters);
93 1128 : }
94 :
95 : void
96 26 : MFEMProblem::addIndicator(const std::string & user_object_name,
97 : const std::string & name,
98 : InputParameters & parameters)
99 : {
100 52 : auto estimator = addObject<MFEMIndicator>(user_object_name, name, parameters).front();
101 :
102 : // construct the estimator itself
103 26 : estimator->createEstimator();
104 26 : }
105 :
106 : void
107 26 : MFEMProblem::addMarker(const std::string & user_object_name,
108 : const std::string & name,
109 : InputParameters & parameters)
110 : {
111 26 : getProblemData().refiner =
112 78 : addObject<MFEMRefinementMarker>(user_object_name, name, parameters).front();
113 26 : }
114 :
115 : void
116 995 : MFEMProblem::addMFEMSolver(const std::string & user_object_name,
117 : const std::string & name,
118 : InputParameters & parameters)
119 : {
120 1990 : auto object = addObject<Moose::MFEM::SolverBase>(user_object_name, name, parameters).front();
121 995 : auto & problem_data = getProblemData();
122 :
123 995 : if (auto lin_solver = std::dynamic_pointer_cast<Moose::MFEM::LinearSolverBase>(object))
124 : {
125 953 : if (problem_data.jacobian_solver)
126 0 : mooseError("Multiple linear solvers provided. '",
127 0 : problem_data.jacobian_solver->name(),
128 : "' and '",
129 0 : lin_solver->name(),
130 : "'");
131 953 : problem_data.jacobian_solver = lin_solver;
132 : }
133 42 : else if (auto nonlinear_solver =
134 42 : std::dynamic_pointer_cast<Moose::MFEM::NonlinearSolverBase>(object);
135 42 : nonlinear_solver)
136 : {
137 42 : if (problem_data.nonlinear_solver)
138 0 : mooseError("Multiple nonlinear solvers provided. '",
139 0 : problem_data.nonlinear_solver->name(),
140 : "' and '",
141 0 : nonlinear_solver->name(),
142 : "'");
143 42 : problem_data.nonlinear_solver = nonlinear_solver;
144 : }
145 : else
146 0 : mooseError(
147 1037 : "Unsupported MFEM solver object type '", user_object_name, "' for solver '", name, "'.");
148 995 : }
149 :
150 : void
151 1332 : MFEMProblem::addBoundaryCondition(const std::string & bc_name,
152 : const std::string & name,
153 : InputParameters & parameters)
154 : {
155 2664 : auto bc = addObject<MFEMBoundaryCondition>(bc_name, name, parameters).front();
156 1332 : const auto & mfem_bc = *bc;
157 :
158 1332 : if (dynamic_cast<const MFEMIntegratedBC *>(&mfem_bc))
159 : {
160 56 : auto integrated_bc = std::dynamic_pointer_cast<MFEMIntegratedBC>(bc);
161 : auto eqsys =
162 56 : std::dynamic_pointer_cast<Moose::MFEM::EquationSystem>(getProblemData().eqn_system);
163 56 : if (eqsys)
164 56 : eqsys->AddIntegratedBC(std::move(integrated_bc));
165 : else
166 0 : mooseError("Cannot add integrated BC with name '" + name +
167 : "' because there is no corresponding equation system.");
168 56 : }
169 1276 : else if (dynamic_cast<const MFEMComplexIntegratedBC *>(&mfem_bc))
170 : {
171 14 : auto integrated_bc = std::dynamic_pointer_cast<MFEMComplexIntegratedBC>(bc);
172 : auto eqsys =
173 14 : std::dynamic_pointer_cast<Moose::MFEM::ComplexEquationSystem>(getProblemData().eqn_system);
174 14 : if (eqsys)
175 14 : eqsys->AddComplexIntegratedBC(std::move(integrated_bc));
176 : else
177 0 : mooseError("Cannot add complex integrated BC with name '" + name +
178 : "' because there is no corresponding equation system.");
179 14 : }
180 1262 : else if (dynamic_cast<const MFEMComplexEssentialBC *>(&mfem_bc))
181 : {
182 63 : auto essential_bc = std::dynamic_pointer_cast<MFEMComplexEssentialBC>(bc);
183 : auto eqsys =
184 63 : std::dynamic_pointer_cast<Moose::MFEM::ComplexEquationSystem>(getProblemData().eqn_system);
185 63 : if (eqsys)
186 63 : eqsys->AddComplexEssentialBCs(std::move(essential_bc));
187 : else
188 0 : mooseError("Cannot add boundary condition with name '" + name +
189 : "' because there is no corresponding equation system.");
190 63 : }
191 1199 : else if (dynamic_cast<const MFEMEssentialBC *>(&mfem_bc))
192 : {
193 1199 : auto essential_bc = std::dynamic_pointer_cast<MFEMEssentialBC>(bc);
194 : auto eqsys =
195 1199 : std::dynamic_pointer_cast<Moose::MFEM::EquationSystem>(getProblemData().eqn_system);
196 1199 : if (eqsys)
197 1199 : eqsys->AddEssentialBC(std::move(essential_bc));
198 : else
199 0 : mooseError("Cannot add boundary condition with name '" + name +
200 : "' because there is no corresponding equation system.");
201 1199 : }
202 : else
203 : {
204 0 : mooseError("Unsupported bc of type '", bc_name, "' and name '", name, "' detected.");
205 : }
206 1332 : }
207 :
208 : void
209 0 : MFEMProblem::addMaterial(const std::string &, const std::string &, InputParameters &)
210 : {
211 0 : mooseError(
212 : "MFEM materials must be added through the 'FunctorMaterials' block and not 'Materials'");
213 : }
214 :
215 : void
216 272 : MFEMProblem::addFunctorMaterial(const std::string & material_name,
217 : const std::string & name,
218 : InputParameters & parameters)
219 : {
220 560 : addObject<MFEMFunctorMaterial>(material_name, name, parameters);
221 256 : }
222 :
223 : void
224 2564 : MFEMProblem::addFESpace(const std::string & type,
225 : const std::string & name,
226 : InputParameters & parameters)
227 : {
228 5128 : auto & mfem_fespace = *addObject<MFEMFESpace>(type, name, parameters).front();
229 :
230 : // Register fespace and associated fe collection.
231 2564 : getProblemData().fecs.Register(name, mfem_fespace.getFEC());
232 2564 : getProblemData().fespaces.Register(name, mfem_fespace.getFESpace());
233 2564 : }
234 :
235 : void
236 1748 : MFEMProblem::addVariable(const std::string & var_type,
237 : const std::string & var_name,
238 : InputParameters & parameters)
239 : {
240 1748 : addGridFunction(var_type, var_name, parameters);
241 : // MOOSE variables store DoFs for the trial variable and its time derivatives up to second order;
242 : // MFEM GridFunctions store data for only one set of DoFs each, so we must add additional
243 : // GridFunctions for time derivatives.
244 1748 : if (isTransient())
245 : {
246 : const auto time_derivative_var_name =
247 217 : getMFEMObject<MFEMVariable>("MooseVariableBase", var_name).getTimeDerivativeName();
248 217 : getProblemData().time_derivative_map.addTimeDerivativeAssociation(var_name,
249 : time_derivative_var_name);
250 217 : addGridFunction(var_type, time_derivative_var_name, parameters);
251 217 : }
252 1748 : }
253 :
254 : void
255 3676 : MFEMProblem::addGridFunction(const std::string & var_type,
256 : const std::string & var_name,
257 : InputParameters & parameters)
258 : {
259 :
260 3676 : if (var_type == "MFEMVariable" || var_type == "MFEMComplexVariable")
261 : {
262 : // Add MFEM variable directly.
263 3571 : if (var_type == "MFEMComplexVariable")
264 189 : addObject<MFEMComplexVariable>(var_type, var_name, parameters);
265 : else
266 10524 : addObject<MFEMVariable>(var_type, var_name, parameters);
267 : }
268 : else
269 : {
270 : // Add MOOSE variable.
271 105 : ExternalProblem::addVariable(var_type, var_name, parameters);
272 :
273 : // Add MFEM variable indirectly ("gridfunction").
274 105 : InputParameters mfem_variable_params = addMFEMFESpaceFromMOOSEVariable(parameters);
275 420 : addObject<MFEMVariable>("MFEMVariable", var_name, mfem_variable_params);
276 105 : }
277 :
278 : // Register gridfunction.
279 3676 : if (var_type == "MFEMComplexVariable")
280 : {
281 : MFEMComplexVariable & mfem_variable =
282 63 : getMFEMObject<MFEMComplexVariable>("MooseVariableBase", var_name);
283 63 : getProblemData().cmplx_gridfunctions.Register(var_name, mfem_variable.getComplexGridFunction());
284 63 : mfem_variable.declareCoefficients();
285 : }
286 : else // must be real, but may have been set up indirectly from a MOOSE variable
287 : {
288 3613 : MFEMVariable & mfem_variable = getMFEMObject<MFEMVariable>("MooseVariableBase", var_name);
289 3613 : getProblemData().gridfunctions.Register(var_name, mfem_variable.getGridFunction());
290 3613 : mfem_variable.declareCoefficients();
291 : }
292 3676 : }
293 :
294 : void
295 1555 : MFEMProblem::addAuxVariable(const std::string & var_type,
296 : const std::string & var_name,
297 : InputParameters & parameters)
298 : {
299 : // We handle MFEM AuxVariables just like MFEM Variables, except
300 : // we do not add additional GridFunctions for time derivatives.
301 1555 : addGridFunction(var_type, var_name, parameters);
302 1555 : }
303 :
304 : void
305 477 : MFEMProblem::addAuxKernel(const std::string & kernel_name,
306 : const std::string & name,
307 : InputParameters & parameters)
308 : {
309 954 : addObject<MFEMExecutedObject>(kernel_name, name, parameters);
310 477 : }
311 :
312 : void
313 1816 : MFEMProblem::addKernel(const std::string & kernel_name,
314 : const std::string & name,
315 : InputParameters & parameters)
316 : {
317 3632 : auto kernel = addObject<MFEMKernel>(kernel_name, name, parameters).front();
318 1816 : const auto & kernel_object = *kernel;
319 :
320 1816 : if (dynamic_cast<const MFEMComplexKernel *>(&kernel_object))
321 : {
322 63 : auto complex_kernel = std::dynamic_pointer_cast<MFEMComplexKernel>(kernel);
323 : auto eqsys =
324 63 : std::dynamic_pointer_cast<Moose::MFEM::ComplexEquationSystem>(getProblemData().eqn_system);
325 63 : if (eqsys)
326 63 : eqsys->AddComplexKernel(std::move(complex_kernel));
327 : else
328 0 : mooseError("Cannot add complex kernel with name '" + name +
329 : "' because there is no corresponding equation system.");
330 63 : }
331 : else
332 : {
333 : auto eqsys =
334 1753 : std::dynamic_pointer_cast<Moose::MFEM::EquationSystem>(getProblemData().eqn_system);
335 1753 : if (eqsys)
336 1753 : eqsys->AddKernel(std::move(kernel));
337 : else
338 0 : mooseError("Cannot add kernel with name '" + name +
339 : "' because there is no corresponding equation system.");
340 1753 : }
341 1816 : }
342 :
343 : void
344 63 : MFEMProblem::addRealComponentToKernel(const std::string & kernel_name,
345 : const std::string & name,
346 : InputParameters & parameters)
347 : {
348 : auto parent_ptr = std::dynamic_pointer_cast<MFEMComplexKernel>(
349 63 : getMFEMObject<MFEMComplexKernel>("Kernel", name).getSharedPtr());
350 252 : parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
351 126 : auto kernel_ptr = addObject<MFEMKernel>(kernel_name, name + "_real", parameters).front();
352 63 : parent_ptr->setRealKernel(kernel_ptr);
353 63 : }
354 :
355 : void
356 56 : MFEMProblem::addImagComponentToKernel(const std::string & kernel_name,
357 : const std::string & name,
358 : InputParameters & parameters)
359 : {
360 : auto parent_ptr = std::dynamic_pointer_cast<MFEMComplexKernel>(
361 56 : getMFEMObject<MFEMComplexKernel>("Kernel", name).getSharedPtr());
362 224 : parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
363 112 : auto kernel_ptr = addObject<MFEMKernel>(kernel_name, name + "_imag", parameters).front();
364 56 : parent_ptr->setImagKernel(kernel_ptr);
365 56 : }
366 :
367 : void
368 0 : MFEMProblem::addRealComponentToBC(const std::string & kernel_name,
369 : const std::string & name,
370 : InputParameters & parameters)
371 : {
372 : auto parent_ptr = std::dynamic_pointer_cast<MFEMComplexIntegratedBC>(
373 0 : getMFEMObject<MFEMComplexIntegratedBC>("BoundaryCondition", name).getSharedPtr());
374 0 : parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
375 0 : parameters.set<std::vector<BoundaryName>>("boundary") =
376 0 : parent_ptr->getParam<std::vector<BoundaryName>>("boundary");
377 : auto bc_ptr = std::dynamic_pointer_cast<MFEMIntegratedBC>(
378 0 : addObject<MFEMBoundaryCondition>(kernel_name, name + "_real", parameters).front());
379 0 : parent_ptr->setRealBC(bc_ptr);
380 0 : }
381 :
382 : void
383 0 : MFEMProblem::addImagComponentToBC(const std::string & kernel_name,
384 : const std::string & name,
385 : InputParameters & parameters)
386 : {
387 : auto parent_ptr = std::dynamic_pointer_cast<MFEMComplexIntegratedBC>(
388 0 : getMFEMObject<MFEMComplexIntegratedBC>("BoundaryCondition", name).getSharedPtr());
389 0 : parameters.set<VariableName>("variable") = parent_ptr->getParam<VariableName>("variable");
390 0 : parameters.set<std::vector<BoundaryName>>("boundary") =
391 0 : parent_ptr->getParam<std::vector<BoundaryName>>("boundary");
392 : auto bc_ptr = std::dynamic_pointer_cast<MFEMIntegratedBC>(
393 0 : addObject<MFEMBoundaryCondition>(kernel_name, name + "_imag", parameters).front());
394 0 : parent_ptr->setImagBC(bc_ptr);
395 0 : }
396 :
397 : int
398 325 : vectorFunctionDim(const std::string & type, const InputParameters & parameters)
399 : {
400 650 : if (parameters.isParamSetByUser("expression_z"))
401 295 : return 3;
402 90 : if (parameters.isParamSetByUser("expression_y") || type == "LevelSetOlssonVortex")
403 28 : return 2;
404 4 : if (parameters.isParamSetByUser("expression_x"))
405 2 : return 1;
406 :
407 0 : return 3;
408 : }
409 :
410 : const std::vector<std::string> SCALAR_FUNCS = {"Axisymmetric2D3DSolutionFunction",
411 : "BicubicSplineFunction",
412 : "CoarsenedPiecewiseLinear",
413 : "CompositeFunction",
414 : "ConstantFunction",
415 : "ImageFunction",
416 : "ParsedFunction",
417 : "ParsedGradFunction",
418 : "PeriodicFunction",
419 : "PiecewiseBilinear",
420 : "PiecewiseConstant",
421 : "PiecewiseConstantFromCSV",
422 : "PiecewiseLinear",
423 : "PiecewiseLinearFromVectorPostprocessor",
424 : "PiecewiseMultiInterpolation",
425 : "PiecewiseMulticonstant",
426 : "SolutionFunction",
427 : "SplineFunction",
428 : "FunctionSeries",
429 : "LevelSetOlssonBubble",
430 : "LevelSetOlssonPlane",
431 : "NearestReporterCoordinatesFunction",
432 : "ParameterMeshFunction",
433 : "ParsedOptimizationFunction",
434 : "FourierNoise",
435 : "MovingPlanarFront",
436 : "MultiControlDrumFunction",
437 : "Grad2ParsedFunction",
438 : "GradParsedFunction",
439 : "ScaledAbsDifferenceDRLRewardFunction",
440 : "CircularAreaHydraulicDiameterFunction",
441 : "CosineHumpFunction",
442 : "CosineTransitionFunction",
443 : "CubicTransitionFunction",
444 : "GeneralizedCircumference",
445 : "PiecewiseFunction",
446 : "TimeRampFunction"},
447 : VECTOR_FUNCS = {"ParsedVectorFunction", "LevelSetOlssonVortex"};
448 :
449 : void
450 1430 : MFEMProblem::addFunction(const std::string & type,
451 : const std::string & name,
452 : InputParameters & parameters)
453 : {
454 1430 : ExternalProblem::addFunction(type, name, parameters);
455 1430 : auto & func = getFunction(name);
456 : // FIXME: Do we want to have optimised versions for when functions
457 : // are only of space or only of time.
458 1430 : if (std::find(SCALAR_FUNCS.begin(), SCALAR_FUNCS.end(), type) != SCALAR_FUNCS.end())
459 : {
460 1011 : getCoefficients().declareScalar<mfem::FunctionCoefficient>(
461 : name,
462 1011 : [&func](const mfem::Vector & p, mfem::real_t t) -> mfem::real_t
463 1935562 : { return func.value(t, Moose::MFEM::libMeshPointFromMFEMVector(p)); });
464 : }
465 419 : else if (std::find(VECTOR_FUNCS.begin(), VECTOR_FUNCS.end(), type) != VECTOR_FUNCS.end())
466 : {
467 325 : int dim = vectorFunctionDim(type, parameters);
468 325 : getCoefficients().declareVector<mfem::VectorFunctionCoefficient>(
469 : name,
470 : dim,
471 325 : [&func, dim](const mfem::Vector & p, mfem::real_t t, mfem::Vector & u)
472 : {
473 : libMesh::RealVectorValue vector_value =
474 1189598 : func.vectorValue(t, Moose::MFEM::libMeshPointFromMFEMVector(p));
475 4440146 : for (int i = 0; i < dim; i++)
476 : {
477 3250548 : u[i] = vector_value(i);
478 : }
479 1189598 : });
480 : }
481 94 : else if ("MFEMParsedFunction" != type)
482 : {
483 2 : mooseWarning("Could not identify whether function ",
484 : type,
485 : " is scalar or vector; no MFEM coefficient object created.");
486 : }
487 1428 : }
488 :
489 : void
490 740 : MFEMProblem::addPostprocessor(const std::string & type,
491 : const std::string & name,
492 : InputParameters & parameters)
493 : {
494 740 : if (parameters.getSystemAttributeName() == "MFEMExecutedObject")
495 : {
496 1288 : checkUserObjectNameCollision(name, "Postprocessor");
497 1288 : addObject<MFEMExecutedObject>(type, name, parameters);
498 644 : const PostprocessorValue & val = getPostprocessorValueByName(name);
499 644 : getCoefficients().declareScalar<mfem::FunctionCoefficient>(
500 646 : name, [&val](const mfem::Vector &) -> mfem::real_t { return val; });
501 : }
502 : else
503 96 : ExternalProblem::addPostprocessor(type, name, parameters);
504 740 : }
505 :
506 : void
507 328 : MFEMProblem::addVectorPostprocessor(const std::string & type,
508 : const std::string & name,
509 : InputParameters & parameters)
510 : {
511 328 : if (parameters.getSystemAttributeName() == "MFEMExecutedObject")
512 : {
513 656 : checkUserObjectNameCollision(name, "VectorPostprocessor");
514 984 : addObject<MFEMExecutedObject>(type, name, parameters);
515 : }
516 : else
517 0 : ExternalProblem::addVectorPostprocessor(type, name, parameters);
518 328 : }
519 :
520 : InputParameters
521 105 : MFEMProblem::addMFEMFESpaceFromMOOSEVariable(InputParameters & parameters)
522 : {
523 :
524 210 : InputParameters fespace_params = _factory.getValidParams("MFEMGenericFESpace");
525 210 : InputParameters variable_params = _factory.getValidParams("MFEMVariable");
526 :
527 105 : const auto family = Utility::string_to_enum<FEFamily>(parameters.get<MooseEnum>("family"));
528 105 : auto order = static_cast<int>(parameters.get<MooseEnum>("order"));
529 105 : const auto dim = mesh().dimension();
530 :
531 105 : std::string space;
532 105 : int vdim = 1;
533 :
534 105 : switch (family)
535 : {
536 19 : case FEFamily::LAGRANGE:
537 19 : space = "H1";
538 19 : break;
539 19 : case FEFamily::NEDELEC_ONE:
540 19 : space = "ND";
541 19 : break;
542 19 : case FEFamily::RAVIART_THOMAS:
543 19 : space = "RT";
544 19 : --order;
545 19 : break;
546 18 : case FEFamily::MONOMIAL:
547 : case FEFamily::L2_LAGRANGE:
548 18 : space = "L2";
549 18 : break;
550 12 : case FEFamily::LAGRANGE_VEC:
551 12 : space = "H1";
552 12 : vdim = dim;
553 12 : break;
554 18 : case FEFamily::MONOMIAL_VEC:
555 : case FEFamily::L2_LAGRANGE_VEC:
556 18 : space = "L2";
557 18 : vdim = dim;
558 18 : break;
559 0 : default:
560 0 : mooseError("Unable to set MFEM FESpace for MOOSE variable");
561 : break;
562 : }
563 :
564 : // Create fespace name. If this already exists, we will reuse this for
565 : // the mfem variable ("gridfunction"). If using AMR, this implies all
566 : // variables sharing the fespace are affected.
567 105 : const auto fec_name = space + "_" + std::to_string(dim) + "D_P" + std::to_string(order);
568 105 : const auto fes_name = fec_name + "_X" + std::to_string(vdim);
569 :
570 : // Set all fespace parameters.
571 105 : fespace_params.set<std::string>("fec_name") = fec_name;
572 315 : fespace_params.set<int>("vdim") = vdim;
573 :
574 210 : if (!hasMFEMObject("MFEMFESpace", fes_name))
575 138 : addFESpace("MFEMGenericFESpace", fes_name, fespace_params);
576 :
577 315 : variable_params.set<MFEMFESpaceName>("fespace") = fes_name;
578 :
579 210 : return variable_params;
580 105 : }
581 :
582 : void
583 2691 : MFEMProblem::displaceMesh()
584 : {
585 : // Displace mesh
586 2691 : if (mesh().shouldDisplace())
587 : {
588 11 : mesh().displace(static_cast<mfem::GridFunction const &>(*getMeshDisplacementGridFunction()));
589 : // TODO: update FESpaces GridFunctions etc for transient solves
590 : }
591 2691 : }
592 :
593 : std::optional<std::reference_wrapper<mfem::ParGridFunction const>>
594 1490 : MFEMProblem::getMeshDisplacementGridFunction()
595 : {
596 : // If C++23 transform were available this would be easier
597 1490 : auto const displacement_variable = mesh().getMeshDisplacementVariable();
598 1490 : if (displacement_variable)
599 : {
600 22 : return *_problem_data.gridfunctions.Get(displacement_variable.value());
601 : }
602 : else
603 : {
604 1468 : return std::nullopt;
605 : }
606 : }
607 :
608 : void
609 0 : MFEMProblem::rebalanceMesh(mfem::ParMesh & pmesh)
610 : {
611 0 : if (pmesh.Nonconforming())
612 : {
613 0 : pmesh.Rebalance();
614 0 : updateFESpaces();
615 0 : updateGridFunctions();
616 : }
617 0 : }
618 :
619 : void
620 13 : MFEMProblem::updateFESpaces()
621 : {
622 39 : for (const auto & fe_space_pair : _problem_data.fespaces)
623 26 : fe_space_pair.second->Update();
624 13 : }
625 :
626 : void
627 26 : MFEMProblem::updateGridFunctions()
628 : {
629 78 : for (const auto & gridfunction_pair : _problem_data.gridfunctions)
630 52 : gridfunction_pair.second->Update();
631 26 : }
632 :
633 : std::vector<VariableName>
634 0 : MFEMProblem::getAuxVariableNames()
635 : {
636 0 : return systemBaseAuxiliary().getVariableNames();
637 : }
638 :
639 : MFEMMesh &
640 65840 : MFEMProblem::mesh()
641 : {
642 : mooseAssert(ExternalProblem::mesh().type() == "MFEMMesh",
643 : "Please choose the MFEMMesh mesh type for an MFEMProblem\n");
644 65840 : return static_cast<MFEMMesh &>(_mesh);
645 : }
646 :
647 : const MFEMMesh &
648 3049 : MFEMProblem::mesh() const
649 : {
650 3049 : return const_cast<MFEMProblem *>(this)->mesh();
651 : }
652 :
653 : void
654 187 : MFEMProblem::addSubMesh(const std::string & var_type,
655 : const std::string & var_name,
656 : InputParameters & parameters)
657 : {
658 374 : auto & mfem_submesh = *addObject<MFEMSubMesh>(var_type, var_name, parameters).front();
659 : // Register submesh.
660 187 : getProblemData().submeshes.Register(var_name, mfem_submesh.getSubMesh());
661 187 : }
662 :
663 : void
664 664 : MFEMProblem::addTransfer(const std::string & transfer_name,
665 : const std::string & name,
666 : InputParameters & parameters)
667 : {
668 664 : if (parameters.getBase() == "MFEMSubMeshTransfer")
669 687 : addObject<MFEMExecutedObject>(transfer_name, name, parameters);
670 : else
671 435 : ExternalProblem::addTransfer(transfer_name, name, parameters);
672 664 : }
673 :
674 : void
675 1086 : MFEMProblem::addInitialCondition(const std::string & ic_name,
676 : const std::string & name,
677 : InputParameters & parameters)
678 : {
679 2172 : addObject<MFEMExecutedObject>(ic_name, name, parameters);
680 1086 : }
681 :
682 : void
683 9993 : MFEMProblem::executeMFEMObjects(const ExecFlagType & exec_type)
684 : {
685 9993 : std::vector<MFEMExecutedObject *> objects;
686 9993 : theWarehouse()
687 9993 : .query()
688 9993 : .condition<AttribSystem>("MFEMExecutedObject")
689 9993 : .condition<AttribExecOns>(exec_type)
690 19986 : .condition<AttribThread>(0)
691 9993 : .queryInto(objects);
692 :
693 9993 : std::map<std::string, const MFEMExecutedObject *> suppliers;
694 13745 : for (auto * const object : objects)
695 7506 : for (const auto & item : object->getSuppliedItems())
696 : {
697 3754 : const auto [it, inserted] = suppliers.emplace(item, object);
698 3754 : if (!inserted && it->second != object)
699 2 : mooseError("MFEM executed-object dependency ambiguity on ",
700 : exec_type,
701 : ": both '",
702 2 : it->second->name(),
703 : "' and '",
704 2 : object->name(),
705 : "' supply '",
706 : item,
707 : "'.");
708 : }
709 :
710 13741 : for (auto * const object : objects)
711 : {
712 3750 : object->initialize();
713 3750 : object->execute();
714 3750 : object->finalize();
715 :
716 3750 : if (auto * const pp = dynamic_cast<const Postprocessor *>(object))
717 : {
718 1170 : _reporter_data.finalize(pp->PPName());
719 1170 : setPostprocessorValueByName(pp->PPName(), pp->getValue());
720 : }
721 :
722 3750 : if (auto * const vpp = dynamic_cast<VectorPostprocessor *>(object))
723 646 : _reporter_data.finalize(vpp->PPName());
724 : }
725 9995 : }
726 :
727 : std::string
728 1461 : MFEMProblem::solverTypeString(const unsigned int libmesh_dbg_var(solver_sys_num))
729 : {
730 : mooseAssert(solver_sys_num == 0, "No support for multi-system with MFEM right now");
731 :
732 1461 : std::vector<std::string> solvers;
733 :
734 1461 : if (getProblemData().nonlinear_solver)
735 42 : solvers.push_back(MooseUtils::prettyCppType(getProblemData().nonlinear_solver.get()));
736 :
737 1461 : if (getProblemData().jacobian_solver)
738 : {
739 979 : solvers.push_back(MooseUtils::prettyCppType(getProblemData().jacobian_solver.get()));
740 979 : if (const auto * prec = getProblemData().jacobian_solver->GetPreconditioner())
741 867 : solvers.push_back(MooseUtils::prettyCppType(prec));
742 : }
743 :
744 5369 : return solvers.empty() ? "None" : MooseUtils::stringJoin(solvers);
745 1461 : }
746 :
747 : bool
748 105 : MFEMProblem::hasMFEMObject(const std::string & system, const std::string & name) const
749 : {
750 105 : std::vector<MooseObject *> objs;
751 105 : theWarehouse()
752 105 : .query()
753 105 : .condition<AttribSystem>(system)
754 210 : .condition<AttribThread>(0)
755 105 : .condition<AttribName>(name)
756 105 : .queryInto(objs);
757 210 : return !objs.empty();
758 105 : }
759 :
760 : #endif
|