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 : #pragma once
11 :
12 : #include "Action.h"
13 : #include "ActionWarehouse.h"
14 : #include "InputParametersChecksUtils.h"
15 : #include "ActionComponent.h"
16 :
17 : // We include these headers for all the derived classes that will be building objects
18 : #include "FEProblemBase.h"
19 : #include "Factory.h"
20 : #include "MultiMooseEnum.h"
21 :
22 : #define registerPhysicsBaseTasks(app_name, derived_name) \
23 : registerMooseAction(app_name, derived_name, "init_physics"); \
24 : registerMooseAction(app_name, derived_name, "copy_vars_physics"); \
25 : registerMooseAction(app_name, derived_name, "check_integrity_early_physics")
26 :
27 : /**
28 : * Base class to help creating an entire physics
29 : */
30 : class PhysicsBase : public Action, public InputParametersChecksUtils<PhysicsBase>
31 : {
32 : public:
33 : static InputParameters validParams();
34 :
35 : PhysicsBase(const InputParameters & parameters);
36 :
37 : /// Provide additional parameters for the relationship managers
38 455 : virtual InputParameters getAdditionalRMParams() const { return emptyInputParameters(); };
39 :
40 : // Responding to tasks //
41 : /// Forwards from the action tasks to the implemented addXYZ() in the derived classes
42 : /// If you need more than these:
43 : /// - register your action to the new task using
44 : /// registerMooseAction("AppName", ActionClass, "task_name");
45 : /// - override actOnAdditionalTasks and add your additional work there
46 : virtual void act() override final;
47 :
48 : /// Routine to add additional setup work on additional registered tasks to a Physics
49 72 : virtual void actOnAdditionalTasks() {}
50 :
51 : // Block restriction //
52 : /**
53 : * @brief Add new blocks to the Physics
54 : * @param blocks list of blocks to add to the physics
55 : */
56 : void addBlocks(const std::vector<SubdomainName> & blocks);
57 : void addBlocksById(const std::vector<SubdomainID> & block_ids);
58 :
59 : /// Return the blocks this physics is defined on
60 : const std::vector<SubdomainName> & blocks() const { return _blocks; }
61 :
62 : /**
63 : * @brief Check if an external object has the same block restriction
64 : * @param object_name name of the object to check the block restriction of
65 : * @param blocks the blocks for this object
66 : * @param error_if_not_identical whether to error if the block restrictions dont match
67 : */
68 : bool checkBlockRestrictionIdentical(const std::string & object_name,
69 : const std::vector<SubdomainName> & blocks,
70 : const bool error_if_not_identical = true) const;
71 :
72 : /**
73 : * Whether the Physics is defined on those blocks
74 : * @param blocks the blocks to check
75 : */
76 : bool hasBlocks(const std::vector<SubdomainName> & blocks) const;
77 :
78 : // Coupling with Physics //
79 : /**
80 : * @brief Get a Physics from the ActionWarehouse with the requested type and name
81 : * @param phys_name name of the Physics to retrieve
82 : * @param allow_fail whether to allow returning a nullptr if the physics does not exist
83 : */
84 : template <typename T>
85 : const T * getCoupledPhysics(const PhysicsName & phys_name, const bool allow_fail = false) const;
86 : /// Get all Physics from the ActionWarehouse with the requested type
87 : template <typename T>
88 : const std::vector<T *> getCoupledPhysics(const bool allow_fail = false) const;
89 :
90 : /// Return the maximum dimension of the blocks the Physics is active on
91 : unsigned int dimension() const;
92 :
93 : // Coupling with Components //
94 : /// Get a component with the requested name
95 : const ActionComponent & getActionComponent(const ComponentName & comp_name) const;
96 : /// Check that the component is of the desired type
97 : template <typename T>
98 : void checkComponentType(const ActionComponent & component) const;
99 : /// Most basic way of adding a component: simply adding the blocks to the block
100 : /// restriction of the Physics. More complex behavior should be implemented by overriding
101 : virtual void addComponent(const ActionComponent & component);
102 :
103 : /// Return the list of solver (nonlinear + linear) variables in this physics
104 482 : const std::vector<VariableName> & solverVariableNames() const { return _solver_var_names; };
105 : /// Return the list of aux variables in this physics
106 240 : const std::vector<VariableName> & auxVariableNames() const { return _aux_var_names; };
107 :
108 : /**
109 : * @brief Return the names of the UserObjects this Physics adds to the problem.
110 : *
111 : * A Physics that adds a UserObject that another object may reference in its constructor must
112 : * declare that UserObject's name here so the construction order can be resolved regardless of
113 : * input file order. This is consulted before the 'add_user_object' task runs, so it must be
114 : * computable without adding the objects (i.e. from the Physics name, blocks and variables, all
115 : * known after init_physics). The default is empty: a Physics that adds no such UserObject need
116 : * not override.
117 : */
118 0 : virtual std::vector<UserObjectName> getSuppliedUserObjects() const { return {}; }
119 :
120 : protected:
121 : /// Return whether the Physics is solved using a transient
122 : bool isTransient() const;
123 :
124 : /// Get the factory for this physics
125 : /// The factory lets you get the parameters for objects
126 2343 : Factory & getFactory() { return _factory; }
127 273 : Factory & getFactory() const { return _factory; }
128 : /// Get the problem for this physics
129 : /// Useful to add objects to the simulation
130 3758 : virtual FEProblemBase & getProblem()
131 : {
132 : mooseAssert(_problem, "Requesting the problem too early");
133 3758 : return *_problem;
134 : }
135 219 : virtual const FEProblemBase & getProblem() const
136 : {
137 : mooseAssert(_problem, "Requesting the problem too early");
138 219 : return *_problem;
139 : }
140 :
141 : /// Tell the app if we want to use Exodus restart
142 : void prepareCopyVariablesFromMesh() const;
143 : /**
144 : * Copy nonlinear or aux variables from the mesh file
145 : *
146 : * @param variables_to_copy Nonlinear or aux (not a mix) variables
147 : * @param are_nonlinear True if \c variables_to_copy are nonlinear; else, aux
148 : */
149 : void copyVariablesFromMesh(const std::vector<VariableName> & variables_to_copy,
150 : bool are_nonlinear = true);
151 :
152 : /// Use prefix() to disambiguate names
153 1599 : std::string prefix() const { return name() + "_"; }
154 :
155 : /**
156 : * @brief Add a UserObject supplied by this Physics to the problem.
157 : *
158 : * Forwards to FEProblemBase::addUserObject. Use this instead of getProblem().addUserObject() so
159 : * that any UserObject this Physics supplies is (in a debug build) checked to have been declared
160 : * in getSuppliedUserObjects(), keeping the declaration and the construction consistent.
161 : */
162 : void
163 : addUserObject(const std::string & uo_type, const std::string & uo_name, InputParameters & params);
164 :
165 : /// Keep track of the name of the solver variable defined in the Physics
166 260 : void saveSolverVariableName(const VariableName & var_name)
167 : {
168 260 : _solver_var_names.push_back(var_name);
169 260 : }
170 : /// Keep track of the name of an aux variable defined in the Physics
171 : void saveAuxVariableName(const VariableName & var_name) { _aux_var_names.push_back(var_name); }
172 :
173 : /// Check whether a variable already exists
174 : bool variableExists(const VariableName & var_name, bool error_if_aux) const;
175 : /// Check whether a variable already exists and is a solver variable
176 : bool solverVariableExists(const VariableName & var_name) const;
177 :
178 : /// Get the solver system for this variable index. The index should be the index of the variable in solver
179 : /// var_names (currently _solver_var_names) vector
180 : const SolverSystemName & getSolverSystem(unsigned int variable_index) const;
181 : /// Get the solver system for this variable name
182 : const SolverSystemName & getSolverSystem(const VariableName & variable_name) const;
183 :
184 : /// Add a new required task for all physics deriving from this class
185 : /// NOTE: This does not register the task, you still need to call registerMooseAction
186 1596 : void addRequiredPhysicsTask(const std::string & task) { _required_tasks.insert(task); }
187 :
188 : /**
189 : * @brief Set the blocks parameter to the input parameters of an object this Physics will create
190 : * @param params the parameters of the object
191 : * @param blocks the blocks to set as the parameter
192 : */
193 : void assignBlocks(InputParameters & params, const std::vector<SubdomainName> & blocks) const;
194 : /**
195 : * @brief Check if a vector contains all the mesh blocks
196 : * @param blocks the vector blocks to check for whether it contains every block in the mesh
197 : */
198 : bool allMeshBlocks(const std::vector<SubdomainName> & blocks) const;
199 : bool allMeshBlocks(const std::set<SubdomainName> & blocks) const;
200 : // These APIs can deal with ANY_BLOCK_ID or ids with no names. They will be slower than the
201 : // MooseMeshUtils' APIs, but are more convenient for setup purposes
202 : /// Get the set of subdomain ids for the incoming vector of subdomain names
203 : std::set<SubdomainID> getSubdomainIDs(const std::set<SubdomainName> & blocks) const;
204 : /// Get the vector of subdomain names and ids for the incoming set of subdomain IDs
205 : std::vector<std::string> getSubdomainNamesAndIDs(const std::set<SubdomainID> & blocks) const;
206 :
207 : /**
208 : * Process the given petsc option pairs into the system solver settings
209 : */
210 : void addPetscPairsToPetscOptions(
211 : const std::vector<std::pair<MooseEnumItem, std::string>> & petsc_pair_options);
212 :
213 : // Helpers to check on variable types
214 : /// Whether the variable is a finite volume variable
215 : bool isVariableFV(const VariableName & var_name) const;
216 : /// Whether the variable is a scalar variable (global single scalar, not a field)
217 : bool isVariableScalar(const VariableName & var_name) const;
218 :
219 : // Routines to help with deciding when to create objects
220 : /**
221 : * Returns whether this Physics should create the variable. Will return false if the variable
222 : * already exists and has the necessary block restriction.
223 : * @param var_name name of the variable
224 : * @param blocks block restriction to use. If empty, no block restriction
225 : * @param error_if_aux error if the variable is auxiliary
226 : */
227 : bool shouldCreateVariable(const VariableName & var_name,
228 : const std::vector<SubdomainName> & blocks,
229 : const bool error_if_aux);
230 :
231 : /**
232 : * Returns whether this Physics should create the variable. Will return false if the initial
233 : * condition already exists and has the necessary block restriction.
234 : * @param var_name name of the variable
235 : * @param blocks block restriction to use. If empty, no block restriction
236 : * @param ic_is_default_ic whether this IC is from a default parameter, and therefore should be
237 : * skipped when recovering/restarting
238 : * @param error_if_already_defined two ICs cannot be defined on the same subdomain, so if this is
239 : * set to true, any overlap between the subdomains of two ICs for the same variable will cause an
240 : * error. If set to false, the existing ICs will take priority, and this routine will return
241 : * false. Setting 'error_if_already_defined' to '!ic_is_default_ic' is a good idea if it is ok to
242 : * overwrite the default IC value of the Physics.
243 : */
244 : bool shouldCreateIC(const VariableName & var_name,
245 : const std::vector<SubdomainName> & blocks,
246 : const bool ic_is_default_ic,
247 : const bool error_if_already_defined) const;
248 :
249 : /**
250 : * Returns whether this Physics should create the variable. Will return false if the time
251 : * derivative kernel already exists and has the necessary block restriction.
252 : * @param var_name name of the variable
253 : * @param blocks block restriction to use. If empty, no block restriction
254 : * @param error_if_already_defined two time derivatives can be defined on the same subdomain, but
255 : * it is usually not correct. So if this is set to true, any overlap between the subdomains of two
256 : * time derivatives for the same variable will cause an error. If set to false, the existing time
257 : * derivative will be deemed as sufficient, and this routine will return false.
258 : */
259 : bool shouldCreateTimeDerivative(const VariableName & var_name,
260 : const std::vector<SubdomainName> & blocks,
261 : const bool error_if_already_defined) const;
262 :
263 : // Other conceivable "shouldCreate" routines for things that are unique to a variable
264 : // - shouldCreateTimeIntegrator
265 : // - shouldCreatePredictor/Corrector
266 :
267 : /**
268 : * When this is called, we are knowingly not using the value of these parameters. This routine
269 : * checks whether these parameters simply have defaults or were passed by the user
270 : * @param param_names the parameters we are ignoring
271 : * @param object_type the type of the object we are not building (thus ignoring the parameters)
272 : * @param object_name the name of the object we are not building
273 : */
274 : void reportPotentiallyMissedParameters(const std::vector<std::string> & param_names,
275 : const std::string & object_type,
276 : const std::string & object_name = "") const;
277 :
278 : /// Additional checks performed near the end of the setup phase
279 0 : virtual void checkIntegrity() const {}
280 :
281 : /// System names for the system(s) owning the solver variables
282 : std::vector<SolverSystemName> _system_names;
283 :
284 : /// System numbers for the system(s) owning the solver variables
285 : std::vector<unsigned int> _system_numbers;
286 :
287 : /// Whether to output additional information
288 : const bool _verbose;
289 :
290 : /// Whether to add a default preconditioning.
291 : /// The implementation of the default is defined by the derived class
292 : const MooseEnum & _preconditioning;
293 :
294 : /// Keep track of the subdomains the Physics is defined on
295 : std::vector<SubdomainName> _blocks;
296 :
297 : private:
298 : /// Gathers additional parameters for the relationship managers from the Physics
299 : /// then calls the parent Action::addRelationshipManagers with those parameters
300 : using Action::addRelationshipManagers;
301 : virtual void addRelationshipManagers(Moose::RelationshipManagerType input_rm_type) override;
302 :
303 : /// Process some parameters that require the problem to be created. Executed on init_physics
304 : void initializePhysics();
305 : /// Additional initialization work that should happen very early, as soon as the problem is created
306 170 : virtual void initializePhysicsAdditional() {}
307 : /// Additional checks performed once the executioner / executor has been created
308 : virtual void checkIntegrityEarly() const;
309 :
310 : /// The default implementation of these routines will do nothing as we do not expect all Physics
311 : /// to be defining an object of every type
312 : /// We keep these private as we don't want a derived class to call a do-nothing implementation
313 : /// If they call a parent class implementation, it must have been re-defined as protected
314 0 : virtual void addSolverVariables() {}
315 0 : virtual void addAuxiliaryVariables() {}
316 3 : virtual void addInitialConditions() {}
317 0 : virtual void addFEKernels() {}
318 0 : virtual void addFVKernels() {}
319 0 : virtual void addFVInterpolationMethods() {}
320 0 : virtual void addNodalKernels() {}
321 0 : virtual void addDiracKernels() {}
322 0 : virtual void addDGKernels() {}
323 0 : virtual void addScalarKernels() {}
324 0 : virtual void addInterfaceKernels() {}
325 0 : virtual void addFVInterfaceKernels() {}
326 0 : virtual void addFEBCs() {}
327 0 : virtual void addFVBCs() {}
328 0 : virtual void addNodalBCs() {}
329 0 : virtual void addPeriodicBCs() {}
330 0 : virtual void addFunctions() {}
331 0 : virtual void addAuxiliaryKernels() {}
332 0 : virtual void addMaterials() {}
333 0 : virtual void addFunctorMaterials() {}
334 0 : virtual void addUserObjects() {}
335 0 : virtual void addCorrectors() {}
336 0 : virtual void addMultiApps() {}
337 0 : virtual void addTransfers() {}
338 0 : virtual void addPostprocessors() {}
339 0 : virtual void addVectorPostprocessors() {}
340 0 : virtual void addReporters() {}
341 0 : virtual void addOutputs() {}
342 0 : virtual void addPreconditioning() {}
343 0 : virtual void addExecutioner() {}
344 0 : virtual void addExecutors() {}
345 :
346 : /// Check the list of required tasks for missing tasks
347 : void checkRequiredTasks() const;
348 :
349 : /// Whether the physics is to be solved as a transient. It can be advantageous to solve
350 : /// some physics directly to steady state
351 : MooseEnum _is_transient;
352 :
353 : /// Vector of the solver variables (nonlinear and linear) in the Physics
354 : std::vector<VariableName> _solver_var_names;
355 : /// Vector of the aux variables in the Physics
356 : std::vector<VariableName> _aux_var_names;
357 :
358 : /// Dimension of the physics, which we expect for now to be the dimension of the mesh
359 : /// NOTE: this is not known at construction time, only after initializePhysics which is a huge bummer
360 : unsigned int _dim = libMesh::invalid_uint;
361 :
362 : /// Manually keeps track of the tasks required by each physics as tasks cannot be inherited
363 : std::set<std::string> _required_tasks;
364 : };
365 :
366 : template <typename T>
367 : const T *
368 : PhysicsBase::getCoupledPhysics(const PhysicsName & phys_name, const bool allow_fail) const
369 : {
370 : constexpr bool is_physics = std::is_base_of<PhysicsBase, T>::value;
371 : libmesh_ignore(is_physics);
372 : mooseAssert(is_physics, "Must be a PhysicsBase to be retrieved by getCoupledPhysics");
373 : const auto all_T_physics = _awh.getActions<T>();
374 : for (const auto * const physics : all_T_physics)
375 : {
376 : if (physics->name() == phys_name)
377 : return physics;
378 : }
379 : if (!allow_fail)
380 : mooseError("Requested Physics '",
381 : phys_name,
382 : "' does not exist or is not of type '",
383 : MooseUtils::prettyCppType<T>(),
384 : "'");
385 : else
386 : return nullptr;
387 : }
388 :
389 : template <typename T>
390 : const std::vector<T *>
391 : PhysicsBase::getCoupledPhysics(const bool allow_fail) const
392 : {
393 : constexpr bool is_physics = std::is_base_of<PhysicsBase, T>::value;
394 : libmesh_ignore(is_physics);
395 : mooseAssert(is_physics, "Must be a PhysicsBase to be retrieved by getCoupledPhysics");
396 : const auto all_T_physics = _awh.getActions<T>();
397 : if (!allow_fail && all_T_physics.empty())
398 : mooseError("No Physics of requested type '", MooseUtils::prettyCppType<T>(), "'");
399 : else
400 : return all_T_physics;
401 : }
402 :
403 : template <typename T>
404 : void
405 : PhysicsBase::checkComponentType(const ActionComponent & component) const
406 : {
407 : if (!dynamic_cast<const T *>(&component))
408 : mooseError("Component '" + component.name() + "' must be of type '" +
409 : MooseUtils::prettyCppType<T>() + "'.\nIt is currently of type '" + component.type() +
410 : "'");
411 : }
|