LCOV - code coverage report
Current view: top level - include/loops - ThreadedElementLoopBase.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 419b9d Lines: 108 113 95.6 %
Date: 2025-08-08 20:01:16 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      138370 :   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       88774 :   virtual void printGeneralExecutionInformation() const {}
     179             : 
     180             :   /// Print information about the particular ordering of objects on each block
     181      309559 :   virtual void printBlockExecutionInformation() const {}
     182             : 
     183             :   /// Print information about the particular ordering of objects on each boundary
     184    14146422 :   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     4255429 : ThreadedElementLoopBase<RangeType>::ThreadedElementLoopBase(MooseMesh & mesh) : _mesh(mesh)
     206             : {
     207     4255429 : }
     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     4597287 : ThreadedElementLoopBase<RangeType>::~ThreadedElementLoopBase()
     218             : {
     219     4597287 : }
     220             : 
     221             : template <typename RangeType>
     222             : void
     223     4530889 : ThreadedElementLoopBase<RangeType>::operator()(const RangeType & range, bool bypass_threading)
     224             : {
     225             :   try
     226             :   {
     227             :     try
     228             :     {
     229     4530889 :       ParallelUniqueId puid;
     230     4530889 :       _tid = bypass_threading ? 0 : puid.id;
     231             : 
     232     4530889 :       pre();
     233     4530889 :       printGeneralExecutionInformation();
     234             : 
     235     4530889 :       _subdomain = Moose::INVALID_BLOCK_ID;
     236     4530889 :       _neighbor_subdomain = Moose::INVALID_BLOCK_ID;
     237     4530889 :       typename RangeType::const_iterator el = range.begin();
     238   437588640 :       for (el = range.begin(); el != range.end(); ++el)
     239             :       {
     240   433057993 :         if (!keepGoing())
     241           5 :           break;
     242             : 
     243   433057988 :         const Elem * elem = *el;
     244             : 
     245   433057988 :         preElement(elem);
     246             : 
     247   433057988 :         _old_subdomain = _subdomain;
     248   433057988 :         _subdomain = elem->subdomain_id();
     249   433057988 :         if (_subdomain != _old_subdomain)
     250             :         {
     251     5722735 :           subdomainChanged();
     252     5722735 :           printBlockExecutionInformation();
     253             :         }
     254             : 
     255   433057988 :         onElement(elem);
     256             : 
     257   866097428 :         if (_mesh.interiorLowerDBlocks().count(elem->subdomain_id()) > 0 ||
     258   866097428 :             _mesh.boundaryLowerDBlocks().count(elem->subdomain_id()) > 0)
     259             :         {
     260       37784 :           postElement(elem);
     261       37784 :           continue;
     262             :         }
     263             : 
     264  2217560489 :         for (unsigned int side = 0; side < elem->n_sides(); side++)
     265             :         {
     266  1784540522 :           std::vector<BoundaryID> boundary_ids = _mesh.getBoundaryIDs(elem, side);
     267  1784540522 :           const Elem * lower_d_elem = _mesh.getLowerDElem(elem, side);
     268             : 
     269  1784540522 :           if (boundary_ids.size() > 0)
     270   130203851 :             for (std::vector<BoundaryID>::iterator it = boundary_ids.begin();
     271   260777632 :                  it != boundary_ids.end();
     272   130573781 :                  ++it)
     273             :             {
     274   130573788 :               preBoundary(elem, side, *it, lower_d_elem);
     275   130573788 :               printBoundaryExecutionInformation(*it);
     276   130573788 :               onBoundary(elem, side, *it, lower_d_elem);
     277             :             }
     278             : 
     279  1784540515 :           const Elem * neighbor = elem->neighbor_ptr(side);
     280  1784540515 :           if (neighbor)
     281             :           {
     282  1652284116 :             preInternalSide(elem, side);
     283             : 
     284  1652284116 :             _old_neighbor_subdomain = _neighbor_subdomain;
     285  1652284116 :             _neighbor_subdomain = neighbor->subdomain_id();
     286  1652284116 :             if (_neighbor_subdomain != _old_neighbor_subdomain)
     287    13999375 :               neighborSubdomainChanged();
     288             : 
     289  1652284116 :             if (shouldComputeInternalSide(*elem, *neighbor))
     290   826133889 :               onInternalSide(elem, side);
     291             : 
     292  1652284116 :             if (boundary_ids.size() > 0)
     293      836988 :               for (std::vector<BoundaryID>::iterator it = boundary_ids.begin();
     294     1679805 :                    it != boundary_ids.end();
     295      842817 :                    ++it)
     296      842817 :                 onInterface(elem, side, *it);
     297             : 
     298  1652284116 :             postInternalSide(elem, side);
     299             :           }
     300             :           else
     301   132256399 :             onExternalSide(elem, side);
     302             :         } // sides
     303             : 
     304   433019967 :         postElement(elem);
     305             :       } // range
     306             : 
     307     4530652 :       post();
     308     4530652 :       resetExecPrintedSets();
     309     4530826 :     }
     310         202 :     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         348 :   catch (MooseException & e)
     320             :   {
     321         174 :     caughtMooseException(e);
     322             :   }
     323     4530826 : }
     324             : 
     325             : template <typename RangeType>
     326             : void
     327     4492774 : ThreadedElementLoopBase<RangeType>::pre()
     328             : {
     329     4492774 : }
     330             : 
     331             : template <typename RangeType>
     332             : void
     333       79978 : ThreadedElementLoopBase<RangeType>::post()
     334             : {
     335       79978 : }
     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      138370 : ThreadedElementLoopBase<RangeType>::preElement(const Elem * /*elem*/)
     346             : {
     347      138370 : }
     348             : 
     349             : template <typename RangeType>
     350             : void
     351    46792289 : ThreadedElementLoopBase<RangeType>::postElement(const Elem * /*elem*/)
     352             : {
     353    46792289 : }
     354             : 
     355             : template <typename RangeType>
     356             : void
     357       44116 : ThreadedElementLoopBase<RangeType>::preBoundary(const Elem * /*elem*/,
     358             :                                                 unsigned int /*side*/,
     359             :                                                 BoundaryID /*bnd_id*/,
     360             :                                                 const Elem * /*lower_d_elem = nullptr*/)
     361             : {
     362       44116 : }
     363             : 
     364             : template <typename RangeType>
     365             : void
     366     6517625 : ThreadedElementLoopBase<RangeType>::onBoundary(const Elem * /*elem*/,
     367             :                                                unsigned int /*side*/,
     368             :                                                BoundaryID /*bnd_id*/,
     369             :                                                const Elem * /*lower_d_elem = nullptr*/)
     370             : {
     371     6517625 : }
     372             : 
     373             : template <typename RangeType>
     374             : void
     375      633322 : ThreadedElementLoopBase<RangeType>::preInternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     376             : {
     377      633322 : }
     378             : 
     379             : template <typename RangeType>
     380             : void
     381  1652191820 : ThreadedElementLoopBase<RangeType>::postInternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     382             : {
     383  1652191820 : }
     384             : 
     385             : template <typename RangeType>
     386             : void
     387    50459559 : ThreadedElementLoopBase<RangeType>::onInternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     388             : {
     389    50459559 : }
     390             : 
     391             : template <typename RangeType>
     392             : void
     393   125647743 : ThreadedElementLoopBase<RangeType>::onExternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     394             : {
     395   125647743 : }
     396             : 
     397             : template <typename RangeType>
     398             : void
     399      276843 : ThreadedElementLoopBase<RangeType>::onInterface(const Elem * /*elem*/,
     400             :                                                 unsigned int /*side*/,
     401             :                                                 BoundaryID /*bnd_id*/)
     402             : {
     403      276843 : }
     404             : 
     405             : template <typename RangeType>
     406             : void
     407      264987 : ThreadedElementLoopBase<RangeType>::subdomainChanged()
     408             : {
     409      264987 : }
     410             : 
     411             : template <typename RangeType>
     412             : void
     413        4480 : ThreadedElementLoopBase<RangeType>::neighborSubdomainChanged()
     414             : {
     415        4480 : }
     416             : 
     417             : template <typename RangeType>
     418             : bool
     419  1645110962 : ThreadedElementLoopBase<RangeType>::shouldComputeInternalSide(const Elem & elem,
     420             :                                                               const Elem & neighbor) const
     421             : {
     422  4935332886 :   auto level = [this](const auto & elem_arg)
     423             :   {
     424  3290221924 :     if (_mesh.doingPRefinement())
     425     7874960 :       return elem_arg.p_level();
     426             :     else
     427  3282346964 :       return elem_arg.level();
     428             :   };
     429  1645110962 :   const auto elem_id = elem.id(), neighbor_id = neighbor.id();
     430  1645110962 :   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  1645110962 :   return (neighbor.active() && (neighbor_level == elem_level) && (elem_id < neighbor_id)) ||
     439  1645110962 :          (neighbor_level < elem_level);
     440             : }
     441             : 
     442             : template <typename RangeType>
     443             : void
     444     4530652 : ThreadedElementLoopBase<RangeType>::resetExecPrintedSets() const
     445             : {
     446     4530652 :   _blocks_exec_printed.clear();
     447     4530652 :   _boundaries_exec_printed.clear();
     448     4530652 : }

Generated by: LCOV version 1.14