https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ComputeUserObjectsThread.C
Go to the documentation of this file.
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
11#include "Problem.h"
12#include "SystemBase.h"
13#include "ElementUserObject.h"
15#include "SideUserObject.h"
16#include "InterfaceUserObject.h"
17#include "ShapeSideUserObject.h"
19#include "NodalUserObject.h"
20#include "SwapBackSentinel.h"
21#include "FEProblem.h"
22#include "MaterialBase.h"
23#include "DomainUserObject.h"
24#include "AuxiliarySystem.h"
25#include "MooseTypes.h"
26
27#include "libmesh/numeric_vector.h"
28
30 const TheWarehouse::Query & query)
31 : ThreadedElementLoop<ConstElemRange>(problem),
32 _query(query),
33 _query_subdomain(_query),
34 _query_boundary(_query),
35 _aux_sys(problem.getAuxiliarySystem())
36{
37}
38
39// Splitting Constructor
41 : ThreadedElementLoop<ConstElemRange>(x._fe_problem),
42 _query(x._query),
43 _query_subdomain(x._query_subdomain),
44 _query_boundary(x._query_boundary),
45 _aux_sys(x._aux_sys)
46{
47}
48
50
51void
53{
54 // for the current thread get block objects for the current subdomain and *all* side objects
55 std::vector<UserObject *> objs;
58 objs);
59
62 .condition<AttribInterfaces>(Interfaces::DomainUserObject)
63 .queryInto(_all_domain_objs);
64
65 std::vector<UserObject *> side_objs;
68 .condition<AttribInterfaces>(Interfaces::SideUserObject)
69 .queryInto(side_objs);
70
71 objs.insert(objs.begin(), side_objs.begin(), side_objs.end());
72
73 // collect dependencies and run subdomain setup
75
76 std::set<MooseVariableFEBase *> needed_moose_vars;
77 std::unordered_set<unsigned int> needed_mat_props;
78 std::set<TagID> needed_fe_var_vector_tags;
79 for (const auto obj : objs)
80 {
81 auto v_obj = dynamic_cast<MooseVariableDependencyInterface *>(obj);
82 if (v_obj)
83 {
84 const auto & v_deps = v_obj->getMooseVariableDependencies();
85 needed_moose_vars.insert(v_deps.begin(), v_deps.end());
86 }
87
88 auto m_obj = dynamic_cast<MaterialPropertyInterface *>(obj);
89 if (m_obj)
90 {
91 auto & m_deps = m_obj->getMatPropDependencies();
92 needed_mat_props.insert(m_deps.begin(), m_deps.end());
93 }
94
95 auto c_obj = dynamic_cast<Coupleable *>(obj);
96 if (c_obj)
97 {
98 const auto & tag_deps = c_obj->getFEVariableCoupleableVectorTags();
99 needed_fe_var_vector_tags.insert(tag_deps.begin(), tag_deps.end());
100 }
101
102 obj->subdomainSetup();
103 }
105 _subdomain, needed_fe_var_vector_tags, _tid);
106
108 _fe_problem.setActiveFEVariableCoupleableVectorTags(needed_fe_var_vector_tags, _tid);
109 _fe_problem.prepareMaterials(needed_mat_props, _subdomain, _tid);
110
115}
116
117void
119{
120 _fe_problem.prepare(elem, _tid);
122
123 // Set up Sentinel class so that, even if reinitMaterials() throws, we
124 // still remember to swap back during stack unwinding.
127
128 for (const auto & uo : _element_objs)
129 {
130 uo->execute();
131
132 // update the aux solution vector if writable coupled variables are used
133 if (uo->hasWritableCoupledVariables())
134 for (auto * var : uo->getWritableCoupledVariables())
135 var->insert(_aux_sys.solution());
136 }
137
138 for (auto & uo : _domain_objs)
139 {
140 uo->preExecuteOnElement();
141 uo->executeOnElement();
142 }
143
144 // UserObject Jacobians
146 {
147 // Prepare shape functions for ShapeElementUserObjects
148 const auto & jacobian_moose_vars = _fe_problem.getUserObjectJacobianVariables(_tid);
149 for (const auto & jvar : jacobian_moose_vars)
150 {
151 unsigned int jvar_id = jvar->number();
152 auto && dof_indices = jvar->dofIndices();
153
155 for (const auto uo : _shape_element_objs)
156 uo->executeJacobianWrapper(jvar_id, dof_indices);
157 }
158 }
159}
160
163 const SubdomainID subdomain_id)
164{
165 const auto cache_key = std::make_pair(bnd_id, subdomain_id);
166 auto [cache_it, inserted] = _boundary_material_reinit_cache.try_emplace(cache_key);
167 auto & cache = cache_it->second;
168
169 if (!inserted)
170 return cache;
171
172 // SideUserObjects are boundary-restricted, not block-restricted. The current subdomain is
173 // accounted for below when selecting the active face materials.
174 std::vector<UserObject *> userobjs;
175 queryBoundary(Interfaces::SideUserObject, bnd_id, userobjs);
176
177 std::vector<const MaterialPropertyInterface *> material_consumers;
178 material_consumers.reserve(userobjs.size() + _domain_objs.size());
179
180 const auto add_material_consumers = [&material_consumers](const auto & objects)
181 {
182 for (const auto * const object : objects)
183 if (const auto * const consumer = dynamic_cast<const MaterialPropertyInterface *>(object))
184 material_consumers.push_back(consumer);
185 };
186
187 add_material_consumers(userobjs);
188 add_material_consumers(_domain_objs);
190 material_consumers, bnd_id, subdomain_id, cache.face_materials, cache.boundary_materials);
191 return cache;
192}
193
194void
196 unsigned int side,
197 BoundaryID bnd_id,
198 const Elem * lower_d_elem /*=nullptr*/)
199{
200 std::vector<UserObject *> userobjs;
201 queryBoundary(Interfaces::SideUserObject, bnd_id, userobjs);
202 if (userobjs.size() == 0 && _domain_objs.size() == 0)
203 return;
204
205 _fe_problem.reinitElemFace(elem, side, _tid);
206
207 // Reinitialize lower-dimensional variables for use in boundary Materials
208 if (lower_d_elem)
209 _fe_problem.reinitLowerDElem(lower_d_elem, _tid);
210
211 const auto & required_mats = getBoundaryMaterialReinitCache(bnd_id, elem->subdomain_id());
212
213 // Set up Sentinel class so that, even if reinitMaterialsFace() throws, we
214 // still remember to swap back during stack unwinding.
216
217 // TODO: Explore whether stateful material property data needs to be swapped in user object
218 // loops by changing swap_stateful=false in the following calls.
220 bnd_id, elem->subdomain_id(), _tid, true, &required_mats.face_materials);
221 _fe_problem.reinitMaterialsBoundary(bnd_id, _tid, true, &required_mats.boundary_materials);
222
223 for (const auto & uo : userobjs)
224 uo->execute();
225
226 for (auto & uo : _domain_objs)
227 {
228 uo->preExecuteOnBoundary();
229 uo->executeOnBoundary();
230 }
231
232 // UserObject Jacobians
233 std::vector<ShapeSideUserObject *> shapers;
235 if (_fe_problem.currentlyComputingJacobian() && shapers.size() > 0)
236 {
237 // Prepare shape functions for ShapeSideUserObjects
238 const auto & jacobian_moose_vars = _fe_problem.getUserObjectJacobianVariables(_tid);
239 for (const auto & jvar : jacobian_moose_vars)
240 {
241 unsigned int jvar_id = jvar->number();
242 auto && dof_indices = jvar->dofIndices();
243
245
246 for (const auto & uo : shapers)
247 uo->executeJacobianWrapper(jvar_id, dof_indices);
248 }
249 }
250}
251
252void
253ComputeUserObjectsThread::onInternalSide(const Elem * elem, unsigned int side)
254{
255 // Pointer to the neighbor we are currently working on.
256 const Elem * neighbor = elem->neighbor_ptr(side);
257
258 if (_internal_side_objs.size() == 0 && _domain_objs.size() == 0)
259 return;
260
262 _fe_problem.reinitNeighbor(elem, side, _tid);
263
264 // Set up Sentinels so that, even if one of the reinitMaterialsXXX() calls throws, we
265 // still remember to swap back during stack unwinding.
267 _fe_problem.reinitMaterialsFace(elem->subdomain_id(), _tid);
268
270 _fe_problem.reinitMaterialsNeighbor(neighbor->subdomain_id(), _tid);
271
272 for (const auto & uo : _internal_side_objs)
273 if (!uo->blockRestricted() || uo->hasBlocks(neighbor->subdomain_id()))
274 uo->execute();
275
276 for (auto & uo : _domain_objs)
277 if (!uo->blockRestricted() || uo->hasBlocks(neighbor->subdomain_id()))
278 {
279 uo->preExecuteOnInternalSide();
280 uo->executeOnInternalSide();
281 }
282}
283
284void
285ComputeUserObjectsThread::onExternalSide(const Elem * elem, unsigned int side)
286{
287 // We are not initializing any materials here because objects that perform calculations should
288 // run onBoundary. onExternalSide should be used for mesh updates (e.g. adding/removing
289 // boundaries). Note that _current_elem / _current_side are not getting updated either.
290 for (auto & uo : _domain_objs)
291 uo->executeOnExternalSide(elem, side);
292}
293
294void
295ComputeUserObjectsThread::onInterface(const Elem * elem, unsigned int side, BoundaryID bnd_id)
296{
297 // Pointer to the neighbor we are currently working on.
298 const Elem * neighbor = elem->neighbor_ptr(side);
299 if (!(neighbor->active()))
300 return;
301
302 std::vector<UserObject *> interface_objs;
303 queryBoundary(Interfaces::InterfaceUserObject, bnd_id, interface_objs);
304
305 bool has_domain_objs = false;
306 // we need to check all domain user objects because a domain user object may not be active
307 // on the current subdomain but should be executed on the interface that it attaches to
308 for (const auto * const domain_uo : _all_domain_objs)
309 if (domain_uo->shouldExecuteOnInterface())
310 {
311 has_domain_objs = true;
312 break;
313 }
314
315 // if we do not have any interface user objects and domain user objects on the current
316 // interface
317 if (interface_objs.empty() && !has_domain_objs)
318 return;
319
321 _fe_problem.reinitNeighbor(elem, side, _tid);
322
323 // Set up Sentinels so that, even if one of the reinitMaterialsXXX() calls throws, we
324 // still remember to swap back during stack unwinding.
325
327 _fe_problem.reinitMaterialsFaceOnBoundary(bnd_id, elem->subdomain_id(), _tid);
329
331 _fe_problem.reinitMaterialsNeighbor(neighbor->subdomain_id(), _tid);
332
333 // Has to happen after face and neighbor properties have been computed. Note that we don't use
334 // a sentinel here because FEProblem::swapBackMaterialsFace is going to handle face materials,
335 // boundary materials, and interface materials (e.g. it queries the boundary material data
336 // with the current element and side
338
339 for (const auto & uo : interface_objs)
340 uo->execute();
341
342 for (auto & uo : _all_domain_objs)
343 if (uo->shouldExecuteOnInterface())
344 {
345 uo->preExecuteOnInterface();
346 uo->executeOnInterface();
347 }
348}
349
350void
356
357void
361
362void
364{
366 {
367 const auto & console = _fe_problem.console();
368 const auto & execute_on = _fe_problem.getCurrentExecuteOnFlag();
369 console << "[DBG] Computing elemental user objects on " << execute_on << std::endl;
370 mooseDoOnce(console << "[DBG] Execution order of objects types on each element then its sides:"
371 << std::endl;
372 // onElement
373 console << "[DBG] - element user objects" << std::endl;
374 console << "[DBG] - domain user objects" << std::endl;
375 console << "[DBG] - element user objects contributing to the Jacobian" << std::endl;
376
377 // onBoundary
378 console << "[DBG] - side user objects" << std::endl;
379 console << "[DBG] - domain user objects executing on sides" << std::endl;
380 console << "[DBG] - side user objects contributing to the Jacobian" << std::endl;
381
382 // onInternalSide
383 console << "[DBG] - internal side user objects" << std::endl;
384 console << "[DBG] - domain user objects executing on internal sides" << std::endl;
385
386 // onInterface
387 console << "[DBG] - interface user objects" << std::endl;
388 console << "[DBG] - domain user objects executing at interfaces" << std::endl;);
389 }
390}
391
392void
394{
396 return;
397
398 // Gather all user objects that may execute
399 // TODO: restrict this gathering of boundary objects to boundaries that are present
400 // in the current block
401 std::vector<ShapeSideUserObject *> shapers;
402 const_cast<ComputeUserObjectsThread *>(this)->queryBoundary(
404
405 std::vector<SideUserObject *> side_uos;
406 const_cast<ComputeUserObjectsThread *>(this)->queryBoundary(
408
409 std::vector<InterfaceUserObject *> interface_objs;
410 const_cast<ComputeUserObjectsThread *>(this)->queryBoundary(
412
413 std::vector<const DomainUserObject *> domain_interface_uos;
414 for (const auto * const domain_uo : _domain_objs)
415 if (domain_uo->shouldExecuteOnInterface())
416 domain_interface_uos.push_back(domain_uo);
417
418 // Approximation of the number of user objects currently executing
419 const auto num_objects = _element_objs.size() + _domain_objs.size() + _shape_element_objs.size() +
420 side_uos.size() + shapers.size() + _internal_side_objs.size() +
421 interface_objs.size() + domain_interface_uos.size();
422
423 const auto & console = _fe_problem.console();
424 const auto & execute_on = _fe_problem.getCurrentExecuteOnFlag();
425
426 if (num_objects > 0)
427 {
429 return;
430
431 console << "[DBG] Ordering of User Objects on block " << _subdomain << std::endl;
432 // Output specific ordering of objects
433 printExecutionOrdering<ElementUserObject>(_element_objs, "element user objects");
434 printExecutionOrdering<DomainUserObject>(_domain_objs, "domain user objects");
436 printExecutionOrdering<ShapeElementUserObject>(
437 _shape_element_objs, "element user objects contributing to the Jacobian");
438 printExecutionOrdering<SideUserObject>(side_uos, "side user objects");
440 printExecutionOrdering<ShapeSideUserObject>(shapers,
441 "side user objects contributing to the Jacobian");
442 printExecutionOrdering<InternalSideUserObject>(_internal_side_objs,
443 "internal side user objects");
444 printExecutionOrdering<InterfaceUserObject>(interface_objs, "interface user objects");
445 console << "[DBG] Only user objects active on local element/sides are executed" << std::endl;
446 }
447 else if (num_objects == 0 && !_blocks_exec_printed.count(_subdomain))
448 console << "[DBG] No User Objects on block " << _subdomain << " on " << execute_on.name()
449 << std::endl;
450
451 // Mark subdomain as having printed to avoid printing again
453}
@ InternalSideUserObject
@ ShapeSideUserObject
@ InterfaceUserObject
@ ShapeElementUserObject
boundary_id_type BoundaryID
subdomain_id_type SubdomainID
Class for threaded computation of UserObjects.
std::vector< DomainUserObject * > _all_domain_objs
std::vector< ElementUserObject * > _element_objs
virtual void post() override
Called after the element range loop.
virtual void onInternalSide(const Elem *elem, unsigned int side) override
Called when doing internal edge assembling.
const TheWarehouse::Query _query
virtual void onElement(const Elem *elem) override
Assembly of the element (not including surface assembly)
virtual void onInterface(const Elem *elem, unsigned int side, BoundaryID bnd_id) override
Called when doing interface assembling.
virtual void onBoundary(const Elem *elem, unsigned int side, BoundaryID bnd_id, const Elem *lower_d_elem=nullptr) override
Called when doing boundary assembling.
void printBlockExecutionInformation() const override
Print information about the loop, mostly order of execution of particular objects.
void queryBoundary(Interfaces iface, BoundaryID bnd, std::vector< T > &results)
ComputeUserObjectsThread(FEProblemBase &problem, const TheWarehouse::Query &query)
void printGeneralExecutionInformation() const override
Print general information about the loop, like the ordering of class of objects.
void join(const ComputeUserObjectsThread &)
const BoundaryMaterialReinitCache & getBoundaryMaterialReinitCache(BoundaryID bnd_id, SubdomainID subdomain_id)
Return the exact face and boundary materials required while executing on this boundary.
std::map< std::pair< BoundaryID, SubdomainID >, BoundaryMaterialReinitCache > _boundary_material_reinit_cache
virtual void onExternalSide(const Elem *elem, unsigned int side) override
Called when iterating over external sides (no side neighbor)
std::vector< ShapeElementUserObject * > _shape_element_objs
void querySubdomain(Interfaces iface, std::vector< T > &results)
std::vector< DomainUserObject * > _domain_objs
virtual void subdomainChanged() override
Called every time the current subdomain changes (i.e.
std::vector< InternalSideUserObject * > _internal_side_objs
Interface for objects that needs coupling capabilities.
Definition Coupleable.h:53
std::set< TagID > & getFEVariableCoupleableVectorTags()
Definition Coupleable.h:120
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
void reinitMaterialsFace(SubdomainID blk_id, const THREAD_ID tid, bool swap_stateful=true, const std::deque< MaterialBase * > *reinit_mats=nullptr)
reinit materials on element faces
virtual void prepareFaceShapes(unsigned int var, const THREAD_ID tid) override
virtual void prepareShapes(unsigned int var, const THREAD_ID tid) override
virtual void reinitElem(const Elem *elem, const THREAD_ID tid) override
virtual void swapBackMaterialsFace(const THREAD_ID tid)
const std::vector< const MooseVariableFEBase * > & getUserObjectJacobianVariables(const THREAD_ID tid) const
const MaterialWarehouse & getMaterialWarehouse() const
void reinitMaterials(SubdomainID blk_id, const THREAD_ID tid, bool swap_stateful=true)
virtual void prepare(const Elem *elem, const THREAD_ID tid) override
void reinitMaterialsNeighbor(SubdomainID blk_id, const THREAD_ID tid, bool swap_stateful=true, const std::deque< MaterialBase * > *reinit_mats=nullptr)
reinit materials on the neighboring element face
virtual void swapBackMaterialsNeighbor(const THREAD_ID tid)
void clearActiveMaterialProperties(const THREAD_ID tid)
Clear the active material properties.
virtual void clearActiveElementalMooseVariables(const THREAD_ID tid) override
Clear the active elemental MooseVariableFEBase.
virtual void prepareFace(const Elem *elem, const THREAD_ID tid) override
void prepareMaterials(const std::unordered_set< unsigned int > &consumer_needed_mat_props, const SubdomainID blk_id, const THREAD_ID tid)
Add the MooseVariables and the material properties that the current materials depend on to the depend...
void reinitElemFace(const Elem *elem, unsigned int side, BoundaryID, const THREAD_ID tid)
virtual void reinitLowerDElem(const Elem *lower_d_elem, const THREAD_ID tid, const std::vector< Point > *const pts=nullptr, const std::vector< Real > *const weights=nullptr) override
const ExecFlagType & getCurrentExecuteOnFlag() const
Return/set the current execution flag.
void reinitMaterialsFaceOnBoundary(const BoundaryID boundary_id, const SubdomainID blk_id, const THREAD_ID tid, const bool swap_stateful=true, const std::deque< MaterialBase * > *const reinit_mats=nullptr)
reinit materials on element faces on a boundary (internal or external) This specific routine helps us...
void reinitMaterialsBoundary(BoundaryID boundary_id, const THREAD_ID tid, bool swap_stateful=true, const std::deque< MaterialBase * > *reinit_mats=nullptr)
reinit materials on a boundary
virtual void setActiveElementalMooseVariables(const std::set< MooseVariableFEBase * > &moose_vars, const THREAD_ID tid) override
Set the MOOSE variables to be reinited on each element.
virtual void swapBackMaterials(const THREAD_ID tid)
virtual void setActiveFEVariableCoupleableVectorTags(std::set< TagID > &vtags, const THREAD_ID tid) override
bool shouldPrintExecution(const THREAD_ID tid) const
Check whether the problem should output execution orders at this time.
virtual void subdomainSetup(SubdomainID subdomain, const THREAD_ID tid)
void reinitMaterialsInterface(BoundaryID boundary_id, const THREAD_ID tid, bool swap_stateful=true)
virtual void reinitNeighbor(const Elem *elem, unsigned int side, const THREAD_ID tid) override
An interface for accessing Materials.
virtual const std::unordered_set< unsigned int > & getMatPropDependencies() const
Retrieve the set of material properties that this object depends on.
void updateBlockFEVariableCoupledVectorTagDependency(SubdomainID id, std::set< TagID > &needed_fe_var_vector_tags, THREAD_ID tid=0) const
const std::set< MooseVariableFieldBase * > & getMooseVariableDependencies() const
Retrieve the set of MooseVariableFieldBase that this object depends on.
const ConsoleStream & console() const
Return console handle.
Definition Problem.h:48
const bool & currentlyComputingJacobian() const
Returns true if the problem is in the process of computing the Jacobian.
Definition SubProblem.h:692
The "SwapBackSentinel" class's destructor guarantees that FEProblemBase::swapBackMaterials{Face,...
NumericVector< Number > & solution()
Definition SystemBase.h:203
QueryCache is a convenient way to construct and pass around (possible partially constructed) warehous...
QueryCache & condition(Args &&... args)
Adds a new condition to the query.
QueryCache clone() const
clone creates and returns an independent copy of the query in its current state.
SubdomainID _subdomain
The subdomain for the current element.
std::set< SubdomainID > _blocks_exec_printed
Keep track of which blocks were visited.
Base class for assembly-like calculations.
void getRequiredBoundaryMaterials(const std::vector< const MaterialPropertyInterface * > &material_consumers, BoundaryID bnd_id, SubdomainID subdomain_id, std::deque< MaterialBase * > &required_face_materials, std::deque< MaterialBase * > &required_boundary_materials) const
Determine the face and boundary materials required by material property consumers.
query_obj query
const BoundaryID ANY_BOUNDARY_ID
Definition MooseTypes.C:21