LCOV - code coverage report
Current view: top level - src/loops - ComputeUserObjectsThread.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 206 211 97.6 %
Date: 2025-07-17 01:28:37 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      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 : }

Generated by: LCOV version 1.14