LCOV - code coverage report
Current view: top level - src/loops - ComputeUserObjectsThread.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 202 205 98.5 %
Date: 2026-05-29 20:35:17 Functions: 14 14 100.0 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.14