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