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 : #include "ComputeUserObjectsThread.h"
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 :
29 205939 : ComputeUserObjectsThread::ComputeUserObjectsThread(FEProblemBase & problem,
30 205939 : const TheWarehouse::Query & query)
31 : : ThreadedElementLoop<ConstElemRange>(problem),
32 205939 : _query(query),
33 205939 : _query_subdomain(_query),
34 205939 : _query_boundary(_query),
35 411878 : _aux_sys(problem.getAuxiliarySystem())
36 : {
37 205939 : }
38 :
39 : // Splitting Constructor
40 18482 : ComputeUserObjectsThread::ComputeUserObjectsThread(ComputeUserObjectsThread & x, Threads::split)
41 : : ThreadedElementLoop<ConstElemRange>(x._fe_problem),
42 18482 : _query(x._query),
43 18482 : _query_subdomain(x._query_subdomain),
44 18482 : _query_boundary(x._query_boundary),
45 36964 : _aux_sys(x._aux_sys)
46 : {
47 18482 : }
48 :
49 242887 : ComputeUserObjectsThread::~ComputeUserObjectsThread() {}
50 :
51 : void
52 348472 : ComputeUserObjectsThread::subdomainChanged()
53 : {
54 : // for the current thread get block objects for the current subdomain and *all* side objects
55 348472 : std::vector<UserObject *> objs;
56 348472 : querySubdomain(Interfaces::ElementUserObject | Interfaces::InternalSideUserObject |
57 : Interfaces::InterfaceUserObject | Interfaces::DomainUserObject,
58 : objs);
59 :
60 348472 : _query.clone()
61 348472 : .condition<AttribThread>(_tid)
62 696944 : .condition<AttribInterfaces>(Interfaces::DomainUserObject)
63 348472 : .queryInto(_all_domain_objs);
64 :
65 348472 : std::vector<UserObject *> side_objs;
66 348472 : _query.clone()
67 348472 : .condition<AttribThread>(_tid)
68 696944 : .condition<AttribInterfaces>(Interfaces::SideUserObject)
69 348472 : .queryInto(side_objs);
70 :
71 348472 : objs.insert(objs.begin(), side_objs.begin(), side_objs.end());
72 :
73 : // collect dependencies and run subdomain setup
74 348472 : _fe_problem.subdomainSetup(_subdomain, _tid);
75 :
76 348472 : std::set<MooseVariableFEBase *> needed_moose_vars;
77 348472 : std::unordered_set<unsigned int> needed_mat_props;
78 348472 : std::set<TagID> needed_fe_var_vector_tags;
79 865687 : for (const auto obj : objs)
80 : {
81 517215 : auto v_obj = dynamic_cast<MooseVariableDependencyInterface *>(obj);
82 517215 : if (v_obj)
83 : {
84 517215 : const auto & v_deps = v_obj->getMooseVariableDependencies();
85 517215 : needed_moose_vars.insert(v_deps.begin(), v_deps.end());
86 : }
87 :
88 517215 : auto m_obj = dynamic_cast<MaterialPropertyInterface *>(obj);
89 517215 : if (m_obj)
90 : {
91 517215 : auto & m_deps = m_obj->getMatPropDependencies();
92 517215 : needed_mat_props.insert(m_deps.begin(), m_deps.end());
93 : }
94 :
95 517215 : auto c_obj = dynamic_cast<Coupleable *>(obj);
96 517215 : if (c_obj)
97 : {
98 517215 : const auto & tag_deps = c_obj->getFEVariableCoupleableVectorTags();
99 517215 : needed_fe_var_vector_tags.insert(tag_deps.begin(), tag_deps.end());
100 : }
101 :
102 517215 : obj->subdomainSetup();
103 : }
104 348472 : _fe_problem.getMaterialWarehouse().updateBlockFEVariableCoupledVectorTagDependency(
105 348472 : _subdomain, needed_fe_var_vector_tags, _tid);
106 :
107 348472 : _fe_problem.setActiveElementalMooseVariables(needed_moose_vars, _tid);
108 348472 : _fe_problem.setActiveFEVariableCoupleableVectorTags(needed_fe_var_vector_tags, _tid);
109 348472 : _fe_problem.prepareMaterials(needed_mat_props, _subdomain, _tid);
110 :
111 348472 : querySubdomain(Interfaces::InternalSideUserObject, _internal_side_objs);
112 348472 : querySubdomain(Interfaces::ElementUserObject, _element_objs);
113 348472 : querySubdomain(Interfaces::ShapeElementUserObject, _shape_element_objs);
114 348472 : querySubdomain(Interfaces::DomainUserObject, _domain_objs);
115 348472 : }
116 :
117 : void
118 16415893 : ComputeUserObjectsThread::onElement(const Elem * elem)
119 : {
120 16415893 : _fe_problem.prepare(elem, _tid);
121 16415893 : _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.
125 16415893 : SwapBackSentinel sentinel(_fe_problem, &FEProblem::swapBackMaterials, _tid);
126 16415893 : _fe_problem.reinitMaterials(_subdomain, _tid);
127 :
128 32897443 : for (const auto & uo : _element_objs)
129 : {
130 16481554 : uo->execute();
131 :
132 : // update the aux solution vector if writable coupled variables are used
133 16481550 : if (uo->hasWritableCoupledVariables())
134 64 : for (auto * var : uo->getWritableCoupledVariables())
135 32 : var->insert(_aux_sys.solution());
136 : }
137 :
138 16424180 : for (auto & uo : _domain_objs)
139 : {
140 8291 : uo->preExecuteOnElement();
141 8291 : uo->executeOnElement();
142 : }
143 :
144 : // UserObject Jacobians
145 16415889 : if (_fe_problem.currentlyComputingJacobian() && _shape_element_objs.size() > 0)
146 : {
147 : // Prepare shape functions for ShapeElementUserObjects
148 838 : const auto & jacobian_moose_vars = _fe_problem.getUserObjectJacobianVariables(_tid);
149 2434 : for (const auto & jvar : jacobian_moose_vars)
150 : {
151 1596 : unsigned int jvar_id = jvar->number();
152 1596 : auto && dof_indices = jvar->dofIndices();
153 :
154 1596 : _fe_problem.prepareShapes(jvar_id, _tid);
155 3192 : for (const auto uo : _shape_element_objs)
156 1596 : uo->executeJacobianWrapper(jvar_id, dof_indices);
157 : }
158 : }
159 16415889 : }
160 :
161 : void
162 6082665 : ComputeUserObjectsThread::onBoundary(const Elem * elem,
163 : unsigned int side,
164 : BoundaryID bnd_id,
165 : const Elem * lower_d_elem /*=nullptr*/)
166 : {
167 6082665 : std::vector<UserObject *> userobjs;
168 6082665 : queryBoundary(Interfaces::SideUserObject, bnd_id, userobjs);
169 6082665 : if (userobjs.size() == 0 && _domain_objs.size() == 0)
170 5233970 : return;
171 :
172 848695 : _fe_problem.reinitElemFace(elem, side, _tid);
173 :
174 : // Reinitialize lower-dimensional variables for use in boundary Materials
175 848695 : if (lower_d_elem)
176 214 : _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.
180 848695 : SwapBackSentinel sentinel(_fe_problem, &FEProblem::swapBackMaterialsFace, _tid);
181 848695 : _fe_problem.reinitMaterialsFaceOnBoundary(bnd_id, elem->subdomain_id(), _tid);
182 848695 : _fe_problem.reinitMaterialsBoundary(bnd_id, _tid);
183 :
184 1746496 : for (const auto & uo : userobjs)
185 897805 : uo->execute();
186 :
187 859236 : for (auto & uo : _domain_objs)
188 : {
189 10545 : uo->preExecuteOnBoundary();
190 10545 : uo->executeOnBoundary();
191 : }
192 :
193 : // UserObject Jacobians
194 848691 : std::vector<ShapeSideUserObject *> shapers;
195 848691 : queryBoundary(Interfaces::ShapeSideUserObject, bnd_id, shapers);
196 848691 : if (_fe_problem.currentlyComputingJacobian() && shapers.size() > 0)
197 : {
198 : // Prepare shape functions for ShapeSideUserObjects
199 434 : const auto & jacobian_moose_vars = _fe_problem.getUserObjectJacobianVariables(_tid);
200 868 : for (const auto & jvar : jacobian_moose_vars)
201 : {
202 434 : unsigned int jvar_id = jvar->number();
203 434 : auto && dof_indices = jvar->dofIndices();
204 :
205 434 : _fe_problem.prepareFaceShapes(jvar_id, _tid);
206 :
207 1302 : for (const auto & uo : shapers)
208 868 : uo->executeJacobianWrapper(jvar_id, dof_indices);
209 : }
210 : }
211 6082661 : }
212 :
213 : void
214 31793648 : ComputeUserObjectsThread::onInternalSide(const Elem * elem, unsigned int side)
215 : {
216 : // Pointer to the neighbor we are currently working on.
217 31793648 : const Elem * neighbor = elem->neighbor_ptr(side);
218 :
219 31793648 : if (_internal_side_objs.size() == 0 && _domain_objs.size() == 0)
220 31770430 : return;
221 :
222 23218 : _fe_problem.prepareFace(elem, _tid);
223 23218 : _fe_problem.reinitNeighbor(elem, side, _tid);
224 :
225 : // Set up Sentinels so that, even if one of the reinitMaterialsXXX() calls throws, we
226 : // still remember to swap back during stack unwinding.
227 23218 : SwapBackSentinel face_sentinel(_fe_problem, &FEProblem::swapBackMaterialsFace, _tid);
228 23218 : _fe_problem.reinitMaterialsFace(elem->subdomain_id(), _tid);
229 :
230 23218 : SwapBackSentinel neighbor_sentinel(_fe_problem, &FEProblem::swapBackMaterialsNeighbor, _tid);
231 23218 : _fe_problem.reinitMaterialsNeighbor(neighbor->subdomain_id(), _tid);
232 :
233 39634 : for (const auto & uo : _internal_side_objs)
234 16416 : if (!uo->blockRestricted() || uo->hasBlocks(neighbor->subdomain_id()))
235 16384 : uo->execute();
236 :
237 36091 : for (auto & uo : _domain_objs)
238 12873 : if (!uo->blockRestricted() || uo->hasBlocks(neighbor->subdomain_id()))
239 : {
240 12580 : uo->preExecuteOnInternalSide();
241 12580 : uo->executeOnInternalSide();
242 : }
243 23218 : }
244 :
245 : void
246 6062366 : ComputeUserObjectsThread::onExternalSide(const Elem * elem, unsigned int side)
247 : {
248 : // We are not initializing any materials here because objects that perform calculations should
249 : // run onBoundary. onExternalSide should be used for mesh updates (e.g. adding/removing
250 : // boundaries). Note that _current_elem / _current_side are not getting updated either.
251 6069532 : for (auto & uo : _domain_objs)
252 7166 : uo->executeOnExternalSide(elem, side);
253 6062366 : }
254 :
255 : void
256 122624 : ComputeUserObjectsThread::onInterface(const Elem * elem, unsigned int side, BoundaryID bnd_id)
257 : {
258 : // Pointer to the neighbor we are currently working on.
259 122624 : const Elem * neighbor = elem->neighbor_ptr(side);
260 122624 : if (!(neighbor->active()))
261 120183 : return;
262 :
263 99920 : std::vector<UserObject *> interface_objs;
264 99920 : queryBoundary(Interfaces::InterfaceUserObject, bnd_id, interface_objs);
265 :
266 99920 : bool has_domain_objs = false;
267 : // we need to check all domain user objects because a domain user object may not be active
268 : // on the current subdomain but should be executed on the interface that it attaches to
269 100132 : for (const auto * const domain_uo : _all_domain_objs)
270 810 : if (domain_uo->shouldExecuteOnInterface())
271 : {
272 598 : has_domain_objs = true;
273 598 : break;
274 : }
275 :
276 : // if we do not have any interface user objects and domain user objects on the current
277 : // interface
278 99920 : if (interface_objs.empty() && !has_domain_objs)
279 97479 : return;
280 :
281 2441 : _fe_problem.prepareFace(elem, _tid);
282 2441 : _fe_problem.reinitNeighbor(elem, side, _tid);
283 :
284 : // Set up Sentinels so that, even if one of the reinitMaterialsXXX() calls throws, we
285 : // still remember to swap back during stack unwinding.
286 :
287 2441 : SwapBackSentinel face_sentinel(_fe_problem, &FEProblem::swapBackMaterialsFace, _tid);
288 2441 : _fe_problem.reinitMaterialsFaceOnBoundary(bnd_id, elem->subdomain_id(), _tid);
289 2441 : _fe_problem.reinitMaterialsBoundary(bnd_id, _tid);
290 :
291 2441 : SwapBackSentinel neighbor_sentinel(_fe_problem, &FEProblem::swapBackMaterialsNeighbor, _tid);
292 2441 : _fe_problem.reinitMaterialsNeighbor(neighbor->subdomain_id(), _tid);
293 :
294 : // Has to happen after face and neighbor properties have been computed. Note that we don't use
295 : // a sentinel here because FEProblem::swapBackMaterialsFace is going to handle face materials,
296 : // boundary materials, and interface materials (e.g. it queries the boundary material data
297 : // with the current element and side
298 2441 : _fe_problem.reinitMaterialsInterface(bnd_id, _tid);
299 :
300 7488 : for (const auto & uo : interface_objs)
301 5047 : uo->execute();
302 :
303 3629 : for (auto & uo : _all_domain_objs)
304 1188 : if (uo->shouldExecuteOnInterface())
305 : {
306 1188 : uo->preExecuteOnInterface();
307 1188 : uo->executeOnInterface();
308 : }
309 99920 : }
310 :
311 : void
312 224413 : ComputeUserObjectsThread::post()
313 : {
314 224413 : _fe_problem.clearActiveElementalMooseVariables(_tid);
315 224413 : _fe_problem.clearActiveMaterialProperties(_tid);
316 224413 : }
317 :
318 : void
319 18480 : ComputeUserObjectsThread::join(const ComputeUserObjectsThread & /*y*/)
320 : {
321 18480 : }
322 :
323 : void
324 224421 : ComputeUserObjectsThread::printGeneralExecutionInformation() const
325 : {
326 224421 : if (_fe_problem.shouldPrintExecution(_tid))
327 : {
328 249 : const auto & console = _fe_problem.console();
329 249 : const auto & execute_on = _fe_problem.getCurrentExecuteOnFlag();
330 249 : console << "[DBG] Computing elemental user objects on " << execute_on << std::endl;
331 249 : mooseDoOnce(console << "[DBG] Execution order of objects types on each element then its sides:"
332 : << std::endl;
333 : // onElement
334 : console << "[DBG] - element user objects" << std::endl;
335 : console << "[DBG] - domain user objects" << std::endl;
336 : console << "[DBG] - element user objects contributing to the Jacobian" << std::endl;
337 :
338 : // onBoundary
339 : console << "[DBG] - side user objects" << std::endl;
340 : console << "[DBG] - domain user objects executing on sides" << std::endl;
341 : console << "[DBG] - side user objects contributing to the Jacobian" << std::endl;
342 :
343 : // onInternalSide
344 : console << "[DBG] - internal side user objects" << std::endl;
345 : console << "[DBG] - domain user objects executing on internal sides" << std::endl;
346 :
347 : // onInterface
348 : console << "[DBG] - interface user objects" << std::endl;
349 : console << "[DBG] - domain user objects executing at interfaces" << std::endl;);
350 : }
351 224421 : }
352 :
353 : void
354 348472 : ComputeUserObjectsThread::printBlockExecutionInformation() const
355 : {
356 348472 : if (!_fe_problem.shouldPrintExecution(_tid))
357 347893 : return;
358 :
359 : // Gather all user objects that may execute
360 : // TODO: restrict this gathering of boundary objects to boundaries that are present
361 : // in the current block
362 1158 : std::vector<ShapeSideUserObject *> shapers;
363 1158 : const_cast<ComputeUserObjectsThread *>(this)->queryBoundary(
364 : Interfaces::ShapeSideUserObject, Moose::ANY_BOUNDARY_ID, shapers);
365 :
366 1158 : std::vector<SideUserObject *> side_uos;
367 1158 : const_cast<ComputeUserObjectsThread *>(this)->queryBoundary(
368 : Interfaces::SideUserObject, Moose::ANY_BOUNDARY_ID, side_uos);
369 :
370 1158 : std::vector<InterfaceUserObject *> interface_objs;
371 1158 : const_cast<ComputeUserObjectsThread *>(this)->queryBoundary(
372 : Interfaces::InterfaceUserObject, Moose::ANY_BOUNDARY_ID, interface_objs);
373 :
374 1158 : std::vector<const DomainUserObject *> domain_interface_uos;
375 1688 : for (const auto * const domain_uo : _domain_objs)
376 530 : if (domain_uo->shouldExecuteOnInterface())
377 142 : domain_interface_uos.push_back(domain_uo);
378 :
379 : // Approximation of the number of user objects currently executing
380 1158 : const auto num_objects = _element_objs.size() + _domain_objs.size() + _shape_element_objs.size() +
381 1158 : side_uos.size() + shapers.size() + _internal_side_objs.size() +
382 1158 : interface_objs.size() + domain_interface_uos.size();
383 :
384 1158 : const auto & console = _fe_problem.console();
385 1158 : const auto & execute_on = _fe_problem.getCurrentExecuteOnFlag();
386 :
387 1158 : if (num_objects > 0)
388 : {
389 1014 : if (_blocks_exec_printed.count(_subdomain))
390 579 : return;
391 :
392 435 : console << "[DBG] Ordering of User Objects on block " << _subdomain << std::endl;
393 : // Output specific ordering of objects
394 870 : printExecutionOrdering<ElementUserObject>(_element_objs, "element user objects");
395 870 : printExecutionOrdering<DomainUserObject>(_domain_objs, "domain user objects");
396 435 : if (_fe_problem.currentlyComputingJacobian())
397 0 : printExecutionOrdering<ShapeElementUserObject>(
398 0 : _shape_element_objs, "element user objects contributing to the Jacobian");
399 870 : printExecutionOrdering<SideUserObject>(side_uos, "side user objects");
400 435 : if (_fe_problem.currentlyComputingJacobian())
401 0 : printExecutionOrdering<ShapeSideUserObject>(shapers,
402 : "side user objects contributing to the Jacobian");
403 870 : printExecutionOrdering<InternalSideUserObject>(_internal_side_objs,
404 : "internal side user objects");
405 870 : printExecutionOrdering<InterfaceUserObject>(interface_objs, "interface user objects");
406 435 : console << "[DBG] Only user objects active on local element/sides are executed" << std::endl;
407 : }
408 144 : else if (num_objects == 0 && !_blocks_exec_printed.count(_subdomain))
409 54 : console << "[DBG] No User Objects on block " << _subdomain << " on " << execute_on.name()
410 54 : << std::endl;
411 :
412 : // Mark subdomain as having printed to avoid printing again
413 579 : _blocks_exec_printed.insert(_subdomain);
414 2895 : }
|