https://mooseframework.inl.gov
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"
14 #include "ShapeElementUserObject.h"
15 #include "SideUserObject.h"
16 #include "InterfaceUserObject.h"
17 #include "ShapeSideUserObject.h"
18 #include "InternalSideUserObject.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)
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 
51 void
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 
60  _query.clone()
62  .condition<AttribInterfaces>(Interfaces::DomainUserObject)
63  .queryInto(_all_domain_objs);
64 
65  std::vector<UserObject *> side_objs;
66  _query.clone()
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 
117 void
119 {
120  _fe_problem.prepare(elem, _tid);
121  _fe_problem.reinitElem(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 
154  _fe_problem.prepareShapes(jvar_id, _tid);
155  for (const auto uo : _shape_element_objs)
156  uo->executeJacobianWrapper(jvar_id, dof_indices);
157  }
158  }
159 }
160 
161 void
163  unsigned int side,
164  BoundaryID bnd_id,
165  const Elem * lower_d_elem /*=nullptr*/)
166 {
167  std::vector<UserObject *> userobjs;
168  queryBoundary(Interfaces::SideUserObject, bnd_id, userobjs);
169  if (userobjs.size() == 0 && _domain_objs.size() == 0)
170  return;
171 
172  _fe_problem.reinitElemFace(elem, side, _tid);
173 
174  // Reinitialize lower-dimensional variables for use in boundary Materials
175  if (lower_d_elem)
176  _fe_problem.reinitLowerDElem(lower_d_elem, _tid);
177 
178  // Set up Sentinel class so that, even if reinitMaterialsFace() throws, we
179  // still remember to swap back during stack unwinding.
181  _fe_problem.reinitMaterialsFaceOnBoundary(bnd_id, elem->subdomain_id(), _tid);
183 
184  for (const auto & uo : userobjs)
185  uo->execute();
186 
187  for (auto & uo : _domain_objs)
188  {
189  uo->preExecuteOnBoundary();
190  uo->executeOnBoundary();
191  }
192 
193  // UserObject Jacobians
194  std::vector<ShapeSideUserObject *> shapers;
196  if (_fe_problem.currentlyComputingJacobian() && shapers.size() > 0)
197  {
198  // Prepare shape functions for ShapeSideUserObjects
199  const auto & jacobian_moose_vars = _fe_problem.getUserObjectJacobianVariables(_tid);
200  for (const auto & jvar : jacobian_moose_vars)
201  {
202  unsigned int jvar_id = jvar->number();
203  auto && dof_indices = jvar->dofIndices();
204 
206 
207  for (const auto & uo : shapers)
208  uo->executeJacobianWrapper(jvar_id, dof_indices);
209  }
210  }
211 }
212 
213 void
214 ComputeUserObjectsThread::onInternalSide(const Elem * elem, unsigned int side)
215 {
216  // Pointer to the neighbor we are currently working on.
217  const Elem * neighbor = elem->neighbor_ptr(side);
218 
219  // Get the global id of the element and the neighbor
220  const dof_id_type elem_id = elem->id(), neighbor_id = neighbor->id();
221 
222  if (_internal_side_objs.size() == 0 && _domain_objs.size() == 0)
223  return;
224  if (!((neighbor->active() && (neighbor->level() == elem->level()) && (elem_id < neighbor_id)) ||
225  (neighbor->level() < elem->level())))
226  return;
227 
229  _fe_problem.reinitNeighbor(elem, side, _tid);
230 
231  // Set up Sentinels so that, even if one of the reinitMaterialsXXX() calls throws, we
232  // still remember to swap back during stack unwinding.
234  _fe_problem.reinitMaterialsFace(elem->subdomain_id(), _tid);
235 
237  _fe_problem.reinitMaterialsNeighbor(neighbor->subdomain_id(), _tid);
238 
239  for (const auto & uo : _internal_side_objs)
240  if (!uo->blockRestricted() || uo->hasBlocks(neighbor->subdomain_id()))
241  uo->execute();
242 
243  for (auto & uo : _domain_objs)
244  if (!uo->blockRestricted() || uo->hasBlocks(neighbor->subdomain_id()))
245  {
246  uo->preExecuteOnInternalSide();
247  uo->executeOnInternalSide();
248  }
249 }
250 
251 void
252 ComputeUserObjectsThread::onExternalSide(const Elem * elem, unsigned int side)
253 {
254  // We are not initializing any materials here because objects that perform calculations should
255  // run onBoundary. onExternalSide should be used for mesh updates (e.g. adding/removing
256  // boundaries). Note that _current_elem / _current_side are not getting updated either.
257  for (auto & uo : _domain_objs)
258  uo->executeOnExternalSide(elem, side);
259 }
260 
261 void
262 ComputeUserObjectsThread::onInterface(const Elem * elem, unsigned int side, BoundaryID bnd_id)
263 {
264  // Pointer to the neighbor we are currently working on.
265  const Elem * neighbor = elem->neighbor_ptr(side);
266  if (!(neighbor->active()))
267  return;
268 
269  std::vector<UserObject *> interface_objs;
270  queryBoundary(Interfaces::InterfaceUserObject, bnd_id, interface_objs);
271 
272  bool has_domain_objs = false;
273  // we need to check all domain user objects because a domain user object may not be active
274  // on the current subdomain but should be executed on the interface that it attaches to
275  for (const auto * const domain_uo : _all_domain_objs)
276  if (domain_uo->shouldExecuteOnInterface())
277  {
278  has_domain_objs = true;
279  break;
280  }
281 
282  // if we do not have any interface user objects and domain user objects on the current
283  // interface
284  if (interface_objs.empty() && !has_domain_objs)
285  return;
286 
288  _fe_problem.reinitNeighbor(elem, side, _tid);
289 
290  // Set up Sentinels so that, even if one of the reinitMaterialsXXX() calls throws, we
291  // still remember to swap back during stack unwinding.
292 
294  _fe_problem.reinitMaterialsFaceOnBoundary(bnd_id, elem->subdomain_id(), _tid);
296 
298  _fe_problem.reinitMaterialsNeighbor(neighbor->subdomain_id(), _tid);
299 
300  // Has to happen after face and neighbor properties have been computed. Note that we don't use
301  // a sentinel here because FEProblem::swapBackMaterialsFace is going to handle face materials,
302  // boundary materials, and interface materials (e.g. it queries the boundary material data
303  // with the current element and side
305 
306  for (const auto & uo : interface_objs)
307  uo->execute();
308 
309  for (auto & uo : _all_domain_objs)
310  if (uo->shouldExecuteOnInterface())
311  {
312  uo->preExecuteOnInterface();
313  uo->executeOnInterface();
314  }
315 }
316 
317 void
319 {
322 }
323 
324 void
326 {
327 }
328 
329 void
331 {
333  {
334  const auto & console = _fe_problem.console();
335  const auto & execute_on = _fe_problem.getCurrentExecuteOnFlag();
336  console << "[DBG] Computing elemental user objects on " << execute_on << std::endl;
337  mooseDoOnce(console << "[DBG] Execution order of objects types on each element then its sides:"
338  << std::endl;
339  // onElement
340  console << "[DBG] - element user objects" << std::endl;
341  console << "[DBG] - domain user objects" << std::endl;
342  console << "[DBG] - element user objects contributing to the Jacobian" << std::endl;
343 
344  // onBoundary
345  console << "[DBG] - side user objects" << std::endl;
346  console << "[DBG] - domain user objects executing on sides" << std::endl;
347  console << "[DBG] - side user objects contributing to the Jacobian" << std::endl;
348 
349  // onInternalSide
350  console << "[DBG] - internal side user objects" << std::endl;
351  console << "[DBG] - domain user objects executing on internal sides" << std::endl;
352 
353  // onInterface
354  console << "[DBG] - interface user objects" << std::endl;
355  console << "[DBG] - domain user objects executing at interfaces" << std::endl;);
356  }
357 }
358 
359 void
361 {
363  return;
364 
365  // Gather all user objects that may execute
366  // TODO: restrict this gathering of boundary objects to boundaries that are present
367  // in the current block
368  std::vector<ShapeSideUserObject *> shapers;
369  const_cast<ComputeUserObjectsThread *>(this)->queryBoundary(
371 
372  std::vector<SideUserObject *> side_uos;
373  const_cast<ComputeUserObjectsThread *>(this)->queryBoundary(
375 
376  std::vector<InterfaceUserObject *> interface_objs;
377  const_cast<ComputeUserObjectsThread *>(this)->queryBoundary(
379 
380  std::vector<const DomainUserObject *> domain_interface_uos;
381  for (const auto * const domain_uo : _domain_objs)
382  if (domain_uo->shouldExecuteOnInterface())
383  domain_interface_uos.push_back(domain_uo);
384 
385  // Approximation of the number of user objects currently executing
386  const auto num_objects = _element_objs.size() + _domain_objs.size() + _shape_element_objs.size() +
387  side_uos.size() + shapers.size() + _internal_side_objs.size() +
388  interface_objs.size() + domain_interface_uos.size();
389 
390  const auto & console = _fe_problem.console();
391  const auto & execute_on = _fe_problem.getCurrentExecuteOnFlag();
392 
393  if (num_objects > 0)
394  {
395  if (_blocks_exec_printed.count(_subdomain))
396  return;
397 
398  console << "[DBG] Ordering of User Objects on block " << _subdomain << std::endl;
399  // Output specific ordering of objects
400  printExecutionOrdering<ElementUserObject>(_element_objs, "element user objects");
401  printExecutionOrdering<DomainUserObject>(_domain_objs, "domain user objects");
403  printExecutionOrdering<ShapeElementUserObject>(
404  _shape_element_objs, "element user objects contributing to the Jacobian");
405  printExecutionOrdering<SideUserObject>(side_uos, "side user objects");
407  printExecutionOrdering<ShapeSideUserObject>(shapers,
408  "side user objects contributing to the Jacobian");
409  printExecutionOrdering<InternalSideUserObject>(_internal_side_objs,
410  "internal side user objects");
411  printExecutionOrdering<InterfaceUserObject>(interface_objs, "interface user objects");
412  console << "[DBG] Only user objects active on local element/sides are executed" << std::endl;
413  }
414  else if (num_objects == 0 && !_blocks_exec_printed.count(_subdomain))
415  console << "[DBG] No User Objects on block " << _subdomain << " on " << execute_on.name()
416  << std::endl;
417 
418  // Mark subdomain as having printed to avoid printing again
420 }
virtual const std::unordered_set< unsigned int > & getMatPropDependencies() const
Retrieve the set of material properties that this object depends on.
const std::set< MooseVariableFieldBase * > & getMooseVariableDependencies() const
Retrieve the set of MooseVariableFieldBase that this object depends on.
virtual void onBoundary(const Elem *elem, unsigned int side, BoundaryID bnd_id, const Elem *lower_d_elem=nullptr) override
Called when doing boundary assembling.
Base class for assembly-like calculations.
virtual void prepareFace(const Elem *elem, const THREAD_ID tid) override
virtual void post() override
Called after the element range loop.
std::vector< ElementUserObject * > _element_objs
std::vector< ShapeElementUserObject * > _shape_element_objs
void querySubdomain(Interfaces iface, std::vector< T > &results)
QueryCache is a convenient way to construct and pass around (possible partially constructed) warehous...
Definition: TheWarehouse.h:208
void printBlockExecutionInformation() const override
Print information about the loop, mostly order of execution of particular objects.
virtual void prepare(const Elem *elem, const THREAD_ID tid) override
NumericVector< Number > & solution()
Definition: SystemBase.h:196
void printGeneralExecutionInformation() const override
Print general information about the loop, like the ordering of class of objects.
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...
const ExecFlagType & getCurrentExecuteOnFlag() const
Return/set the current execution flag.
virtual void onElement(const Elem *elem) override
Assembly of the element (not including surface assembly)
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 onInterface(const Elem *elem, unsigned int side, BoundaryID bnd_id) override
Called when doing interface assembling.
void queryBoundary(Interfaces iface, BoundaryID bnd, std::vector< T > &results)
void clearActiveMaterialProperties(const THREAD_ID tid)
Clear the active material properties.
virtual void subdomainChanged() override
Called every time the current subdomain changes (i.e.
const MaterialWarehouse & getMaterialWarehouse() const
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
ComputeUserObjectsThread(FEProblemBase &problem, const TheWarehouse::Query &query)
virtual void swapBackMaterialsFace(const THREAD_ID tid)
virtual void prepareFaceShapes(unsigned int var, const THREAD_ID tid) override
virtual void setActiveElementalMooseVariables(const std::set< MooseVariableFEBase *> &moose_vars, const THREAD_ID tid) override
Set the MOOSE variables to be reinited on each element.
const std::vector< const MooseVariableFEBase * > & getUserObjectJacobianVariables(const THREAD_ID tid) const
bool shouldPrintExecution(const THREAD_ID tid) const
Check whether the problem should output execution orders at this time.
virtual void reinitElem(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
boundary_id_type BoundaryID
virtual void onExternalSide(const Elem *elem, unsigned int side) override
Called when iterating over external sides (no side neighbor)
const TheWarehouse::Query _query
void join(const ComputeUserObjectsThread &)
QueryCache clone() const
clone creates and returns an independent copy of the query in its current state.
Definition: TheWarehouse.h:292
virtual void swapBackMaterialsNeighbor(const THREAD_ID tid)
query_obj query
std::set< SubdomainID > _blocks_exec_printed
Keep track of which blocks were visited.
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
Interface for objects that needs coupling capabilities.
Definition: Coupleable.h:52
const ConsoleStream & console() const
Return console handle.
Definition: Problem.h:48
virtual void swapBackMaterials(const THREAD_ID tid)
void updateBlockFEVariableCoupledVectorTagDependency(SubdomainID id, std::set< TagID > &needed_fe_var_vector_tags, THREAD_ID tid=0) const
Update FE variable coupleable vector tag vector.
virtual void subdomainSetup(SubdomainID subdomain, const THREAD_ID tid)
An interface for accessing Materials.
void reinitMaterials(SubdomainID blk_id, const THREAD_ID tid, bool swap_stateful=true)
Class for threaded computation of UserObjects.
virtual void onInternalSide(const Elem *elem, unsigned int side) override
Called when doing internal edge assembling.
std::vector< InternalSideUserObject * > _internal_side_objs
void reinitElemFace(const Elem *elem, unsigned int side, BoundaryID, const THREAD_ID tid)
QueryCache & condition(Args &&... args)
Adds a new condition to the query.
Definition: TheWarehouse.h:284
SubdomainID _subdomain
The subdomain for the current element.
std::vector< DomainUserObject * > _domain_objs
virtual void reinitNeighbor(const Elem *elem, unsigned int side, const THREAD_ID tid) override
const bool & currentlyComputingJacobian() const
Returns true if the problem is in the process of computing the Jacobian.
Definition: SubProblem.h:684
virtual void setActiveFEVariableCoupleableVectorTags(std::set< TagID > &vtags, const THREAD_ID tid) override
virtual void prepareShapes(unsigned int var, const THREAD_ID tid) override
void reinitMaterialsInterface(BoundaryID boundary_id, const THREAD_ID tid, bool swap_stateful=true)
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...
std::set< TagID > & getFEVariableCoupleableVectorTags()
Definition: Coupleable.h:120
virtual void clearActiveElementalMooseVariables(const THREAD_ID tid) override
Clear the active elemental MooseVariableFEBase.
const BoundaryID ANY_BOUNDARY_ID
Definition: MooseTypes.C:21
uint8_t dof_id_type
std::vector< DomainUserObject * > _all_domain_objs
The "SwapBackSentinel" class&#39;s destructor guarantees that FEProblemBase::swapBackMaterials{Face,Neighbor}() is called even when an exception is thrown from FEProblemBase::reinitMaterials{Face,Neighbor}.