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