LCOV - code coverage report
Current view: top level - include/loops - ThreadedElementLoopBase.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 8601ad Lines: 108 113 95.6 %
Date: 2025-07-18 13:27:08 Functions: 48 79 60.8 %
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             : #pragma once
      11             : 
      12             : #include "ParallelUniqueId.h"
      13             : #include "MooseMesh.h"
      14             : #include "MooseTypes.h"
      15             : #include "MooseException.h"
      16             : #include "libmesh/libmesh_exceptions.h"
      17             : #include "libmesh/elem.h"
      18             : 
      19             : /**
      20             :  * Base class for assembly-like calculations.
      21             :  */
      22             : template <typename RangeType>
      23             : class ThreadedElementLoopBase
      24             : {
      25             : public:
      26             :   ThreadedElementLoopBase(MooseMesh & mesh);
      27             : 
      28             :   ThreadedElementLoopBase(ThreadedElementLoopBase & x, Threads::split split);
      29             : 
      30             :   virtual ~ThreadedElementLoopBase();
      31             : 
      32             :   virtual void operator()(const RangeType & range, bool bypass_threading = false);
      33             : 
      34             :   /**
      35             :    * Called before the element range loop
      36             :    */
      37             :   virtual void pre();
      38             : 
      39             :   /**
      40             :    * Called after the element range loop
      41             :    */
      42             :   virtual void post();
      43             : 
      44             :   /**
      45             :    * Assembly of the element (not including surface assembly)
      46             :    *
      47             :    * @param elem - active element
      48             :    */
      49             :   virtual void onElement(const Elem * elem);
      50             : 
      51             :   /**
      52             :    * Called before the element assembly
      53             :    *
      54             :    * @param elem - active element
      55             :    */
      56             :   virtual void preElement(const Elem * elem);
      57             : 
      58             :   /**
      59             :    * Called after the element assembly is done (including surface assembling)
      60             :    *
      61             :    * @param elem - active element
      62             :    */
      63             :   virtual void postElement(const Elem * elem);
      64             : 
      65             :   /**
      66             :    * Called before the boundary assembly
      67             :    *
      68             :    * @param elem - The element we are checking is on the boundary.
      69             :    * @param side - The side of the element in question.
      70             :    * @param bnd_id - ID of the boundary we are at
      71             :    * @param lower_d_elem - Lower dimensional element (e.g. Mortar)
      72             :    */
      73             :   virtual void preBoundary(const Elem * elem,
      74             :                            unsigned int side,
      75             :                            BoundaryID bnd_id,
      76             :                            const Elem * lower_d_elem = nullptr);
      77             : 
      78             :   /**
      79             :    * Called when doing boundary assembling
      80             :    *
      81             :    * @param elem - The element we are checking is on the boundary.
      82             :    * @param side - The side of the element in question.
      83             :    * @param bnd_id - ID of the boundary we are at
      84             :    * @param lower_d_elem - Lower dimensional element (e.g. Mortar)
      85             :    */
      86             :   virtual void onBoundary(const Elem * elem,
      87             :                           unsigned int side,
      88             :                           BoundaryID bnd_id,
      89             :                           const Elem * lower_d_elem = nullptr);
      90             : 
      91             :   /**
      92             :    * Called before evaluations on an element internal side
      93             :    *
      94             :    * @param elem - Element we are on
      95             :    * @param side - local side number of the element 'elem'
      96             :    */
      97             :   virtual void preInternalSide(const Elem * elem, unsigned int side);
      98             : 
      99             :   /**
     100             :    * Called after evaluations on an element internal side
     101             :    *
     102             :    * @param elem - Element we are on
     103             :    * @param side - local side number of the element 'elem'
     104             :    */
     105             :   virtual void postInternalSide(const Elem * elem, unsigned int side);
     106             : 
     107             :   /**
     108             :    * Called when doing internal edge assembling
     109             :    *
     110             :    * @param elem - Element we are on
     111             :    * @param side - local side number of the element 'elem'
     112             :    */
     113             :   virtual void onInternalSide(const Elem * elem, unsigned int side);
     114             : 
     115             :   /**
     116             :    * Called when iterating over external sides (no side neighbor)
     117             :    *
     118             :    * @param elem - Element we are on
     119             :    * @param side - local side number of the element 'elem'
     120             :    */
     121             :   virtual void onExternalSide(const Elem * elem, unsigned int side);
     122             : 
     123             :   /**
     124             :    * Called when doing interface assembling
     125             :    *
     126             :    * @param elem - Element we are on
     127             :    * @param side - local side number of the element 'elem'
     128             :    * @param bnd_id - ID of the interface we are at
     129             :    */
     130             :   virtual void onInterface(const Elem * elem, unsigned int side, BoundaryID bnd_id);
     131             : 
     132             :   /**
     133             :    * Called every time the current subdomain changes (i.e. the subdomain of _this_ element
     134             :    * is not the same as the subdomain of the last element).  Beware of over-using this!
     135             :    * You might think that you can do some expensive stuff in here and get away with it...
     136             :    * but there are applications that have TONS of subdomains....
     137             :    */
     138             :   virtual void subdomainChanged();
     139             : 
     140             :   /**
     141             :    * Called every time the neighbor subdomain changes (i.e. the subdomain of _this_ neighbor
     142             :    * is not the same as the subdomain of the last neighbor).  Beware of over-using this!
     143             :    * You might think that you can do some expensive stuff in here and get away with it...
     144             :    * but there are applications that have TONS of subdomains....
     145             :    */
     146             :   virtual void neighborSubdomainChanged();
     147             : 
     148             :   /**
     149             :    * Called if a MooseException is caught anywhere during the computation.
     150             :    * The single input parameter taken is a MooseException object.
     151             :    */
     152           0 :   virtual void caughtMooseException(MooseException &){};
     153             : 
     154             :   /**
     155             :    * Whether or not the loop should continue.
     156             :    *
     157             :    * @return true to keep going, false to stop.
     158             :    */
     159      130106 :   virtual bool keepGoing() { return true; }
     160             : 
     161             : protected:
     162             :   MooseMesh & _mesh;
     163             :   THREAD_ID _tid;
     164             : 
     165             :   /// The subdomain for the current element
     166             :   SubdomainID _subdomain;
     167             : 
     168             :   /// The subdomain for the last element
     169             :   SubdomainID _old_subdomain;
     170             : 
     171             :   /// The subdomain for the current neighbor
     172             :   SubdomainID _neighbor_subdomain;
     173             : 
     174             :   /// The subdomain for the last neighbor
     175             :   SubdomainID _old_neighbor_subdomain;
     176             : 
     177             :   /// Print information about the loop ordering
     178       82655 :   virtual void printGeneralExecutionInformation() const {}
     179             : 
     180             :   /// Print information about the particular ordering of objects on each block
     181      281093 :   virtual void printBlockExecutionInformation() const {}
     182             : 
     183             :   /// Print information about the particular ordering of objects on each boundary
     184    11949927 :   virtual void printBoundaryExecutionInformation(const unsigned int /*bid*/) const {}
     185             : 
     186             :   /// Keep track of which blocks were visited
     187             :   mutable std::set<SubdomainID> _blocks_exec_printed;
     188             : 
     189             :   /// Keep track of which boundaries were visited
     190             :   mutable std::set<BoundaryID> _boundaries_exec_printed;
     191             : 
     192             :   /// Resets the set of blocks and boundaries visited
     193             :   void resetExecPrintedSets() const;
     194             : 
     195             :   /**
     196             :    * Whether to compute the internal side for the provided element-neighbor pair. Typically this
     197             :    * will return true if the element id is less than the neighbor id when the elements are equal
     198             :    * level, or when the element is more refined than the neighbor, and then false otherwise. One
     199             :    * type of loop where the logic will be different is when projecting stateful material properties
     200             :    */
     201             :   virtual bool shouldComputeInternalSide(const Elem & elem, const Elem & neighbor) const;
     202             : };
     203             : 
     204             : template <typename RangeType>
     205     3923358 : ThreadedElementLoopBase<RangeType>::ThreadedElementLoopBase(MooseMesh & mesh) : _mesh(mesh)
     206             : {
     207     3923358 : }
     208             : 
     209             : template <typename RangeType>
     210          61 : ThreadedElementLoopBase<RangeType>::ThreadedElementLoopBase(ThreadedElementLoopBase & x,
     211             :                                                             Threads::split /*split*/)
     212          61 :   : _mesh(x._mesh)
     213             : {
     214          61 : }
     215             : 
     216             : template <typename RangeType>
     217     4265769 : ThreadedElementLoopBase<RangeType>::~ThreadedElementLoopBase()
     218             : {
     219     4265769 : }
     220             : 
     221             : template <typename RangeType>
     222             : void
     223     4206318 : ThreadedElementLoopBase<RangeType>::operator()(const RangeType & range, bool bypass_threading)
     224             : {
     225             :   try
     226             :   {
     227             :     try
     228             :     {
     229     4206318 :       ParallelUniqueId puid;
     230     4206318 :       _tid = bypass_threading ? 0 : puid.id;
     231             : 
     232     4206318 :       pre();
     233     4206318 :       printGeneralExecutionInformation();
     234             : 
     235     4206318 :       _subdomain = Moose::INVALID_BLOCK_ID;
     236     4206318 :       _neighbor_subdomain = Moose::INVALID_BLOCK_ID;
     237     4206318 :       typename RangeType::const_iterator el = range.begin();
     238   392823626 :       for (el = range.begin(); el != range.end(); ++el)
     239             :       {
     240   388617543 :         if (!keepGoing())
     241           5 :           break;
     242             : 
     243   388617538 :         const Elem * elem = *el;
     244             : 
     245   388617538 :         preElement(elem);
     246             : 
     247   388617538 :         _old_subdomain = _subdomain;
     248   388617538 :         _subdomain = elem->subdomain_id();
     249   388617538 :         if (_subdomain != _old_subdomain)
     250             :         {
     251     5296735 :           subdomainChanged();
     252     5296735 :           printBlockExecutionInformation();
     253             :         }
     254             : 
     255   388617538 :         onElement(elem);
     256             : 
     257   777218520 :         if (_mesh.interiorLowerDBlocks().count(elem->subdomain_id()) > 0 ||
     258   777218520 :             _mesh.boundaryLowerDBlocks().count(elem->subdomain_id()) > 0)
     259             :         {
     260       33640 :           postElement(elem);
     261       33640 :           continue;
     262             :         }
     263             : 
     264  1984866154 :         for (unsigned int side = 0; side < elem->n_sides(); side++)
     265             :         {
     266  1596282486 :           std::vector<BoundaryID> boundary_ids = _mesh.getBoundaryIDs(elem, side);
     267  1596282486 :           const Elem * lower_d_elem = _mesh.getLowerDElem(elem, side);
     268             : 
     269  1596282486 :           if (boundary_ids.size() > 0)
     270   116013272 :             for (std::vector<BoundaryID>::iterator it = boundary_ids.begin();
     271   232365470 :                  it != boundary_ids.end();
     272   116352198 :                  ++it)
     273             :             {
     274   116352205 :               preBoundary(elem, side, *it, lower_d_elem);
     275   116352205 :               printBoundaryExecutionInformation(*it);
     276   116352205 :               onBoundary(elem, side, *it, lower_d_elem);
     277             :             }
     278             : 
     279  1596282479 :           const Elem * neighbor = elem->neighbor_ptr(side);
     280  1596282479 :           if (neighbor)
     281             :           {
     282  1478472052 :             preInternalSide(elem, side);
     283             : 
     284  1478472052 :             _old_neighbor_subdomain = _neighbor_subdomain;
     285  1478472052 :             _neighbor_subdomain = neighbor->subdomain_id();
     286  1478472052 :             if (_neighbor_subdomain != _old_neighbor_subdomain)
     287    12818419 :               neighborSubdomainChanged();
     288             : 
     289  1478472052 :             if (shouldComputeInternalSide(*elem, *neighbor))
     290   739362288 :               onInternalSide(elem, side);
     291             : 
     292  1478472052 :             if (boundary_ids.size() > 0)
     293      769094 :               for (std::vector<BoundaryID>::iterator it = boundary_ids.begin();
     294     1543444 :                    it != boundary_ids.end();
     295      774350 :                    ++it)
     296      774350 :                 onInterface(elem, side, *it);
     297             : 
     298  1478472052 :             postInternalSide(elem, side);
     299             :           }
     300             :           else
     301   117810427 :             onExternalSide(elem, side);
     302             :         } // sides
     303             : 
     304   388583668 :         postElement(elem);
     305             :       } // range
     306             : 
     307     4206088 :       post();
     308     4206088 :       resetExecPrintedSets();
     309     4206254 :     }
     310         194 :     catch (libMesh::LogicError & e)
     311             :     {
     312          28 :       mooseException("We caught a libMesh error in ThreadedElementLoopBase:", e.what());
     313             :     }
     314           0 :     catch (MetaPhysicL::LogicError & e)
     315             :     {
     316           0 :       moose::translateMetaPhysicLError(e);
     317             :     }
     318             :   }
     319         332 :   catch (MooseException & e)
     320             :   {
     321         166 :     caughtMooseException(e);
     322             :   }
     323     4206254 : }
     324             : 
     325             : template <typename RangeType>
     326             : void
     327     4170768 : ThreadedElementLoopBase<RangeType>::pre()
     328             : {
     329     4170768 : }
     330             : 
     331             : template <typename RangeType>
     332             : void
     333       74497 : ThreadedElementLoopBase<RangeType>::post()
     334             : {
     335       74497 : }
     336             : 
     337             : template <typename RangeType>
     338             : void
     339           0 : ThreadedElementLoopBase<RangeType>::onElement(const Elem * /*elem*/)
     340             : {
     341           0 : }
     342             : 
     343             : template <typename RangeType>
     344             : void
     345      130106 : ThreadedElementLoopBase<RangeType>::preElement(const Elem * /*elem*/)
     346             : {
     347      130106 : }
     348             : 
     349             : template <typename RangeType>
     350             : void
     351    40317165 : ThreadedElementLoopBase<RangeType>::postElement(const Elem * /*elem*/)
     352             : {
     353    40317165 : }
     354             : 
     355             : template <typename RangeType>
     356             : void
     357       40420 : ThreadedElementLoopBase<RangeType>::preBoundary(const Elem * /*elem*/,
     358             :                                                 unsigned int /*side*/,
     359             :                                                 BoundaryID /*bnd_id*/,
     360             :                                                 const Elem * /*lower_d_elem = nullptr*/)
     361             : {
     362       40420 : }
     363             : 
     364             : template <typename RangeType>
     365             : void
     366     5844747 : ThreadedElementLoopBase<RangeType>::onBoundary(const Elem * /*elem*/,
     367             :                                                unsigned int /*side*/,
     368             :                                                BoundaryID /*bnd_id*/,
     369             :                                                const Elem * /*lower_d_elem = nullptr*/)
     370             : {
     371     5844747 : }
     372             : 
     373             : template <typename RangeType>
     374             : void
     375      596774 : ThreadedElementLoopBase<RangeType>::preInternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     376             : {
     377      596774 : }
     378             : 
     379             : template <typename RangeType>
     380             : void
     381  1478384292 : ThreadedElementLoopBase<RangeType>::postInternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     382             : {
     383  1478384292 : }
     384             : 
     385             : template <typename RangeType>
     386             : void
     387    45362165 : ThreadedElementLoopBase<RangeType>::onInternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     388             : {
     389    45362165 : }
     390             : 
     391             : template <typename RangeType>
     392             : void
     393   112629251 : ThreadedElementLoopBase<RangeType>::onExternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     394             : {
     395   112629251 : }
     396             : 
     397             : template <typename RangeType>
     398             : void
     399      252232 : ThreadedElementLoopBase<RangeType>::onInterface(const Elem * /*elem*/,
     400             :                                                 unsigned int /*side*/,
     401             :                                                 BoundaryID /*bnd_id*/)
     402             : {
     403      252232 : }
     404             : 
     405             : template <typename RangeType>
     406             : void
     407      240959 : ThreadedElementLoopBase<RangeType>::subdomainChanged()
     408             : {
     409      240959 : }
     410             : 
     411             : template <typename RangeType>
     412             : void
     413        4015 : ThreadedElementLoopBase<RangeType>::neighborSubdomainChanged()
     414             : {
     415        4015 : }
     416             : 
     417             : template <typename RangeType>
     418             : bool
     419  1471845104 : ThreadedElementLoopBase<RangeType>::shouldComputeInternalSide(const Elem & elem,
     420             :                                                               const Elem & neighbor) const
     421             : {
     422  4415535312 :   auto level = [this](const auto & elem_arg)
     423             :   {
     424  2943690208 :     if (_mesh.doingPRefinement())
     425     6865316 :       return elem_arg.p_level();
     426             :     else
     427  2936824892 :       return elem_arg.level();
     428             :   };
     429  1471845104 :   const auto elem_id = elem.id(), neighbor_id = neighbor.id();
     430  1471845104 :   const auto elem_level = level(elem), neighbor_level = level(neighbor);
     431             : 
     432             :   // When looping over elements and then sides, we need to make sure that we do not duplicate
     433             :   // effort, e.g. if a face is shared by element 1 and element 2, then we do not want to do compute
     434             :   // work both when we are visiting element 1 *and* then later when visiting element 2. Our rule is
     435             :   // to only compute when we are visiting the element that has the lower element id when element and
     436             :   // neighbor are of the same adaptivity level, and then if they are not of the same level, then
     437             :   // we only compute when we are visiting the finer element
     438  1471845104 :   return (neighbor.active() && (neighbor_level == elem_level) && (elem_id < neighbor_id)) ||
     439  1471845104 :          (neighbor_level < elem_level);
     440             : }
     441             : 
     442             : template <typename RangeType>
     443             : void
     444     4206088 : ThreadedElementLoopBase<RangeType>::resetExecPrintedSets() const
     445             : {
     446     4206088 :   _blocks_exec_printed.clear();
     447     4206088 :   _boundaries_exec_printed.clear();
     448     4206088 : }

Generated by: LCOV version 1.14