LCOV - code coverage report
Current view: top level - include/loops - ThreadedElementLoopBase.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 108 112 96.4 %
Date: 2026-05-29 20:35:17 Functions: 46 76 60.5 %
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             : // C++
      20             : #include <cstring> // for "Jacobian" exception test
      21             : 
      22             : /**
      23             :  * Base class for assembly-like calculations.
      24             :  */
      25             : template <typename RangeType>
      26             : class ThreadedElementLoopBase
      27             : {
      28             : public:
      29             :   ThreadedElementLoopBase(MooseMesh & mesh);
      30             : 
      31             :   ThreadedElementLoopBase(ThreadedElementLoopBase & x, Threads::split split);
      32             : 
      33             :   virtual ~ThreadedElementLoopBase();
      34             : 
      35             :   virtual void operator()(const RangeType & range, bool bypass_threading = false);
      36             : 
      37             :   /**
      38             :    * Called before the element range loop
      39             :    */
      40             :   virtual void pre();
      41             : 
      42             :   /**
      43             :    * Called after the element range loop
      44             :    */
      45             :   virtual void post();
      46             : 
      47             :   /**
      48             :    * Assembly of the element (not including surface assembly)
      49             :    *
      50             :    * @param elem - active element
      51             :    */
      52             :   virtual void onElement(const Elem * elem);
      53             : 
      54             :   /**
      55             :    * Called before the element assembly
      56             :    *
      57             :    * @param elem - active element
      58             :    */
      59             :   virtual void preElement(const Elem * elem);
      60             : 
      61             :   /**
      62             :    * Called after the element assembly is done (including surface assembling)
      63             :    *
      64             :    * @param elem - active element
      65             :    */
      66             :   virtual void postElement(const Elem * elem);
      67             : 
      68             :   /**
      69             :    * Called before the boundary assembly
      70             :    *
      71             :    * @param elem - The element we are checking is on the boundary.
      72             :    * @param side - The side of the element in question.
      73             :    * @param bnd_id - ID of the boundary we are at
      74             :    * @param lower_d_elem - Lower dimensional element (e.g. Mortar)
      75             :    */
      76             :   virtual void preBoundary(const Elem * elem,
      77             :                            unsigned int side,
      78             :                            BoundaryID bnd_id,
      79             :                            const Elem * lower_d_elem = nullptr);
      80             : 
      81             :   /**
      82             :    * Called when doing boundary assembling
      83             :    *
      84             :    * @param elem - The element we are checking is on the boundary.
      85             :    * @param side - The side of the element in question.
      86             :    * @param bnd_id - ID of the boundary we are at
      87             :    * @param lower_d_elem - Lower dimensional element (e.g. Mortar)
      88             :    */
      89             :   virtual void onBoundary(const Elem * elem,
      90             :                           unsigned int side,
      91             :                           BoundaryID bnd_id,
      92             :                           const Elem * lower_d_elem = nullptr);
      93             : 
      94             :   /**
      95             :    * Called before evaluations on an element internal side
      96             :    *
      97             :    * @param elem - Element we are on
      98             :    * @param side - local side number of the element 'elem'
      99             :    */
     100             :   virtual void preInternalSide(const Elem * elem, unsigned int side);
     101             : 
     102             :   /**
     103             :    * Called after evaluations on an element internal side
     104             :    *
     105             :    * @param elem - Element we are on
     106             :    * @param side - local side number of the element 'elem'
     107             :    */
     108             :   virtual void postInternalSide(const Elem * elem, unsigned int side);
     109             : 
     110             :   /**
     111             :    * Called when doing internal edge assembling
     112             :    *
     113             :    * @param elem - Element we are on
     114             :    * @param side - local side number of the element 'elem'
     115             :    */
     116             :   virtual void onInternalSide(const Elem * elem, unsigned int side);
     117             : 
     118             :   /**
     119             :    * Called when iterating over external sides (no side neighbor)
     120             :    *
     121             :    * @param elem - Element we are on
     122             :    * @param side - local side number of the element 'elem'
     123             :    */
     124             :   virtual void onExternalSide(const Elem * elem, unsigned int side);
     125             : 
     126             :   /**
     127             :    * Called when doing interface assembling
     128             :    *
     129             :    * @param elem - Element we are on
     130             :    * @param side - local side number of the element 'elem'
     131             :    * @param bnd_id - ID of the interface we are at
     132             :    */
     133             :   virtual void onInterface(const Elem * elem, unsigned int side, BoundaryID bnd_id);
     134             : 
     135             :   /**
     136             :    * Called every time the current subdomain changes (i.e. the subdomain of _this_ element
     137             :    * is not the same as the subdomain of the last element).  Beware of over-using this!
     138             :    * You might think that you can do some expensive stuff in here and get away with it...
     139             :    * but there are applications that have TONS of subdomains....
     140             :    */
     141             :   virtual void subdomainChanged();
     142             : 
     143             :   /**
     144             :    * Called every time the neighbor subdomain changes (i.e. the subdomain of _this_ neighbor
     145             :    * is not the same as the subdomain of the last neighbor).  Beware of over-using this!
     146             :    * You might think that you can do some expensive stuff in here and get away with it...
     147             :    * but there are applications that have TONS of subdomains....
     148             :    */
     149             :   virtual void neighborSubdomainChanged();
     150             : 
     151             :   /**
     152             :    * Called if a MooseException is caught anywhere during the computation.
     153             :    * The single input parameter taken is a MooseException object.
     154             :    */
     155           0 :   virtual void caughtMooseException(MooseException &) {};
     156             : 
     157             :   /**
     158             :    * Whether or not the loop should continue.
     159             :    *
     160             :    * @return true to keep going, false to stop.
     161             :    */
     162      120424 :   virtual bool keepGoing() { return true; }
     163             : 
     164             : protected:
     165             :   MooseMesh & _mesh;
     166             :   THREAD_ID _tid;
     167             : 
     168             :   /// The subdomain for the current element
     169             :   SubdomainID _subdomain;
     170             : 
     171             :   /// The subdomain for the last element
     172             :   SubdomainID _old_subdomain;
     173             : 
     174             :   /// The subdomain for the current neighbor
     175             :   SubdomainID _neighbor_subdomain;
     176             : 
     177             :   /// The subdomain for the last neighbor
     178             :   SubdomainID _old_neighbor_subdomain;
     179             : 
     180             :   /// Print information about the loop ordering
     181       88577 :   virtual void printGeneralExecutionInformation() const {}
     182             : 
     183             :   /// Print information about the particular ordering of objects on each block
     184      386858 :   virtual void printBlockExecutionInformation() const {}
     185             : 
     186             :   /// Print information about the particular ordering of objects on each boundary
     187    12938294 :   virtual void printBoundaryExecutionInformation(const unsigned int /*bid*/) const {}
     188             : 
     189             :   /// Keep track of which blocks were visited
     190             :   mutable std::set<SubdomainID> _blocks_exec_printed;
     191             : 
     192             :   /// Keep track of which boundaries were visited
     193             :   mutable std::set<BoundaryID> _boundaries_exec_printed;
     194             : 
     195             :   /// Resets the set of blocks and boundaries visited
     196             :   void resetExecPrintedSets() const;
     197             : 
     198             :   /**
     199             :    * Whether to compute the internal side for the provided element-neighbor pair. Typically this
     200             :    * will return true if the element id is less than the neighbor id when the elements are equal
     201             :    * level, or when the element is more refined than the neighbor, and then false otherwise. One
     202             :    * type of loop where the logic will be different is when projecting stateful material properties
     203             :    */
     204             :   virtual bool shouldComputeInternalSide(const Elem & elem, const Elem & neighbor) const;
     205             : };
     206             : 
     207             : template <typename RangeType>
     208     3949533 : ThreadedElementLoopBase<RangeType>::ThreadedElementLoopBase(MooseMesh & mesh) : _mesh(mesh)
     209             : {
     210     3949533 : }
     211             : 
     212             : template <typename RangeType>
     213          61 : ThreadedElementLoopBase<RangeType>::ThreadedElementLoopBase(ThreadedElementLoopBase & x,
     214             :                                                             Threads::split /*split*/)
     215          61 :   : _mesh(x._mesh)
     216             : {
     217          61 : }
     218             : 
     219             : template <typename RangeType>
     220     4307849 : ThreadedElementLoopBase<RangeType>::~ThreadedElementLoopBase()
     221             : {
     222     4307849 : }
     223             : 
     224             : template <typename RangeType>
     225             : void
     226     4173104 : ThreadedElementLoopBase<RangeType>::operator()(const RangeType & range, bool bypass_threading)
     227             : {
     228             :   try
     229             :   {
     230             :     try
     231             :     {
     232     4173104 :       ParallelUniqueId puid;
     233     4173104 :       _tid = bypass_threading ? 0 : puid.id;
     234             : 
     235     4173104 :       pre();
     236     4173104 :       printGeneralExecutionInformation();
     237             : 
     238     4173104 :       _subdomain = Moose::INVALID_BLOCK_ID;
     239     4173104 :       _neighbor_subdomain = Moose::INVALID_BLOCK_ID;
     240     4173104 :       typename RangeType::const_iterator el = range.begin();
     241   761884948 :       for (el = range.begin(); el != range.end(); ++el)
     242             :       {
     243   378872985 :         if (!keepGoing())
     244          10 :           break;
     245             : 
     246   378872975 :         const Elem * elem = *el;
     247             : 
     248   378872975 :         preElement(elem);
     249             : 
     250   378872975 :         _old_subdomain = _subdomain;
     251   378872975 :         _subdomain = elem->subdomain_id();
     252   378872975 :         if (_subdomain != _old_subdomain)
     253             :         {
     254     5483929 :           subdomainChanged();
     255     5483929 :           printBlockExecutionInformation();
     256             :         }
     257             : 
     258   378872975 :         onElement(elem);
     259             : 
     260   757729402 :         if (_mesh.interiorLowerDBlocks().count(elem->subdomain_id()) > 0 ||
     261   757729402 :             _mesh.boundaryLowerDBlocks().count(elem->subdomain_id()) > 0)
     262             :         {
     263       33664 :           postElement(elem);
     264       33664 :           continue;
     265             :         }
     266             : 
     267   378839096 :         const auto elem_boundary_ids = _mesh.getBoundaryIDs(elem);
     268  1949002430 :         for (unsigned int side = 0; side < elem->n_sides(); side++)
     269             :         {
     270  1570163340 :           const auto & boundary_ids = elem_boundary_ids[side];
     271  1570163340 :           const Elem * lower_d_elem = _mesh.getLowerDElem(elem, side);
     272             : 
     273  1687782689 :           for (const auto bnd_id : boundary_ids)
     274             :           {
     275   117619355 :             preBoundary(elem, side, bnd_id, lower_d_elem);
     276   117619355 :             printBoundaryExecutionInformation(bnd_id);
     277   117619355 :             onBoundary(elem, side, bnd_id, lower_d_elem);
     278             :           }
     279             : 
     280  1570163334 :           const Elem * neighbor = elem->neighbor_ptr(side);
     281  1570163334 :           if (neighbor)
     282             :           {
     283  1450991432 :             preInternalSide(elem, side);
     284             : 
     285  1450991432 :             _old_neighbor_subdomain = _neighbor_subdomain;
     286  1450991432 :             _neighbor_subdomain = neighbor->subdomain_id();
     287  1450991432 :             if (_neighbor_subdomain != _old_neighbor_subdomain)
     288    14000149 :               neighborSubdomainChanged();
     289             : 
     290  1450991432 :             if (shouldComputeInternalSide(*elem, *neighbor))
     291    90795762 :               onInternalSide(elem, side);
     292             : 
     293  1451797826 :             for (const auto bnd_id : boundary_ids)
     294      806394 :               onInterface(elem, side, bnd_id);
     295             : 
     296  1450991432 :             postInternalSide(elem, side);
     297             :           }
     298             :           else
     299   119171902 :             onExternalSide(elem, side);
     300             :         } // sides
     301             : 
     302   378839090 :         postElement(elem);
     303             :       } // range
     304             : 
     305     4172883 :       post();
     306     4172883 :       resetExecPrintedSets();
     307     4173040 :     }
     308         157 :     catch (MetaPhysicL::LogicError & e)
     309             :     {
     310           0 :       moose::translateMetaPhysicLError(e);
     311             :     }
     312         314 :     catch (std::exception & e)
     313             :     {
     314             :       // Continue if we find a libMesh degenerate map exception, but
     315             :       // just re-throw for any real error
     316         271 :       if (!strstr(e.what(), "Jacobian") && !strstr(e.what(), "singular") &&
     317         114 :           !strstr(e.what(), "det != 0"))
     318         114 :         throw; // not "throw e;" - that destroys type info!
     319             : 
     320          43 :       mooseException("We caught a libMesh degeneracy exception in ThreadedElementLoopBase:\n",
     321             :                      e.what());
     322             :     }
     323             :   }
     324         314 :   catch (MooseException & e)
     325             :   {
     326         157 :     caughtMooseException(e);
     327             :   }
     328     4173040 : }
     329             : 
     330             : template <typename RangeType>
     331             : void
     332     4137806 : ThreadedElementLoopBase<RangeType>::pre()
     333             : {
     334     4137806 : }
     335             : 
     336             : template <typename RangeType>
     337             : void
     338       78751 : ThreadedElementLoopBase<RangeType>::post()
     339             : {
     340       78751 : }
     341             : 
     342             : template <typename RangeType>
     343             : void
     344           0 : ThreadedElementLoopBase<RangeType>::onElement(const Elem * /*elem*/)
     345             : {
     346           0 : }
     347             : 
     348             : template <typename RangeType>
     349             : void
     350      120424 : ThreadedElementLoopBase<RangeType>::preElement(const Elem * /*elem*/)
     351             : {
     352      120424 : }
     353             : 
     354             : template <typename RangeType>
     355             : void
     356    41100447 : ThreadedElementLoopBase<RangeType>::postElement(const Elem * /*elem*/)
     357             : {
     358    41100447 : }
     359             : 
     360             : template <typename RangeType>
     361             : void
     362       38480 : ThreadedElementLoopBase<RangeType>::preBoundary(const Elem * /*elem*/,
     363             :                                                 unsigned int /*side*/,
     364             :                                                 BoundaryID /*bnd_id*/,
     365             :                                                 const Elem * /*lower_d_elem = nullptr*/)
     366             : {
     367       38480 : }
     368             : 
     369             : template <typename RangeType>
     370             : void
     371     5956030 : ThreadedElementLoopBase<RangeType>::onBoundary(const Elem * /*elem*/,
     372             :                                                unsigned int /*side*/,
     373             :                                                BoundaryID /*bnd_id*/,
     374             :                                                const Elem * /*lower_d_elem = nullptr*/)
     375             : {
     376     5956030 : }
     377             : 
     378             : template <typename RangeType>
     379             : void
     380      551844 : ThreadedElementLoopBase<RangeType>::preInternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     381             : {
     382      551844 : }
     383             : 
     384             : template <typename RangeType>
     385             : void
     386  1450893384 : ThreadedElementLoopBase<RangeType>::postInternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     387             : {
     388  1450893384 : }
     389             : 
     390             : template <typename RangeType>
     391             : void
     392    45450500 : ThreadedElementLoopBase<RangeType>::onInternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     393             : {
     394    45450500 : }
     395             : 
     396             : template <typename RangeType>
     397             : void
     398     7052096 : ThreadedElementLoopBase<RangeType>::onExternalSide(const Elem * /*elem*/, unsigned int /*side*/)
     399             : {
     400     7052096 : }
     401             : 
     402             : template <typename RangeType>
     403             : void
     404      250943 : ThreadedElementLoopBase<RangeType>::onInterface(const Elem * /*elem*/,
     405             :                                                 unsigned int /*side*/,
     406             :                                                 BoundaryID /*bnd_id*/)
     407             : {
     408      250943 : }
     409             : 
     410             : template <typename RangeType>
     411             : void
     412      298822 : ThreadedElementLoopBase<RangeType>::subdomainChanged()
     413             : {
     414      298822 : }
     415             : 
     416             : template <typename RangeType>
     417             : void
     418        3935 : ThreadedElementLoopBase<RangeType>::neighborSubdomainChanged()
     419             : {
     420        3935 : }
     421             : 
     422             : template <typename RangeType>
     423             : bool
     424   177820683 : ThreadedElementLoopBase<RangeType>::shouldComputeInternalSide(const Elem & elem,
     425             :                                                               const Elem & neighbor) const
     426             : {
     427             :   // If we're going to compute the internal side with this elem-neighbor pair, then they must both
     428             :   // be active. Note that if elem is an active coarse element at an interface with finer elements,
     429             :   // then its neighbor will be an equal-level inactive element, hence the following
     430             :   // neighbor.active() check. In that case we'll catch current 'elem' when we come around and
     431             :   // examine one of the finer elements as 'elem'
     432             :   mooseAssert(elem.active(), "This method should never be called with an inactive element");
     433   177820683 :   if (!neighbor.active())
     434      486373 :     return false;
     435             : 
     436             :   //
     437             :   // Define an ordering: first prefer finer by h (higher level()), then prefer finer by p (higher
     438             :   // p_level()), then prefer smaller id()"
     439             :   //
     440             : 
     441   177334310 :   const auto elem_level = elem.level(), neighbor_level = neighbor.level();
     442   177334310 :   if (elem_level != neighbor_level)
     443     1135176 :     return elem_level > neighbor_level;
     444             : 
     445   176199134 :   const auto elem_p_level = elem.p_level(), neighbor_p_level = neighbor.p_level();
     446   176199134 :   if (elem_p_level != neighbor_p_level)
     447       85418 :     return elem_p_level > neighbor_p_level;
     448             : 
     449   176113716 :   return elem.id() < neighbor.id();
     450             : }
     451             : 
     452             : template <typename RangeType>
     453             : void
     454     4172883 : ThreadedElementLoopBase<RangeType>::resetExecPrintedSets() const
     455             : {
     456     4172883 :   _blocks_exec_printed.clear();
     457     4172883 :   _boundaries_exec_printed.clear();
     458     4172883 : }

Generated by: LCOV version 1.14