LCOV - code coverage report
Current view: top level - include/utils - PiecewiseByBlockLambdaFunctor.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 82 85 96.5 %
Date: 2025-07-17 01:28:37 Functions: 218 349 62.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 "MooseMesh.h"
      13             : #include "MooseTypes.h"
      14             : #include "MooseError.h"
      15             : #include "MooseFunctor.h"
      16             : #include "Moose.h"
      17             : #include "Limiter.h"
      18             : #include "MathFVUtils.h"
      19             : #include "GreenGaussGradient.h"
      20             : 
      21             : #include "libmesh/elem.h"
      22             : #include "libmesh/remote_elem.h"
      23             : #include "libmesh/tensor_tools.h"
      24             : 
      25             : #include <unordered_map>
      26             : #include <functional>
      27             : 
      28             : /**
      29             :  * A material property that is evaluated on-the-fly via calls to various overloads of \p operator()
      30             :  */
      31             : template <typename T>
      32             : class PiecewiseByBlockLambdaFunctor : public Moose::FunctorBase<T>
      33             : {
      34             : public:
      35             :   template <typename PolymorphicLambda>
      36             :   PiecewiseByBlockLambdaFunctor(const std::string & name,
      37             :                                 PolymorphicLambda my_lammy,
      38             :                                 const std::set<ExecFlagType> & clearance_schedule,
      39             :                                 const MooseMesh & mesh,
      40             :                                 const std::set<SubdomainID> & block_ids);
      41             : 
      42             :   /**
      43             :    * Set the functor that will be used in calls to \p evaluate overloads
      44             :    * @param mesh The mesh that the functor is defined on
      45             :    * @param block_ids The block/subdomain IDs that the user-provided functor is valid for
      46             :    * @param my_lammy The functor that defines how this object is evaluated
      47             :    */
      48             :   template <typename PolymorphicLambda>
      49             :   void setFunctor(const MooseMesh & mesh,
      50             :                   const std::set<SubdomainID> & block_ids,
      51             :                   PolymorphicLambda my_lammy);
      52             : 
      53       16153 :   virtual ~PiecewiseByBlockLambdaFunctor() = default;
      54             : 
      55             :   bool isExtrapolatedBoundaryFace(const FaceInfo & fi,
      56             :                                   const Elem * elem,
      57             :                                   const Moose::StateArg & time) const override;
      58             : 
      59             :   bool hasBlocks(SubdomainID id) const override;
      60             : 
      61           0 :   bool supportsFaceArg() const override final { return true; }
      62           0 :   bool supportsElemSideQpArg() const override final { return true; }
      63             : 
      64             :   using typename Moose::FunctorBase<T>::FunctorType;
      65             :   using typename Moose::FunctorBase<T>::ValueType;
      66             :   using typename Moose::FunctorBase<T>::DotType;
      67             :   using typename Moose::FunctorBase<T>::GradientType;
      68             : 
      69             : protected:
      70             :   using ElemFn = std::function<T(const Moose::ElemArg &, const Moose::StateArg &)>;
      71             :   using FaceFn = std::function<T(const Moose::FaceArg &, const Moose::StateArg &)>;
      72             :   using ElemQpFn = std::function<T(const Moose::ElemQpArg &, const Moose::StateArg &)>;
      73             :   using ElemSideQpFn = std::function<T(const Moose::ElemSideQpArg &, const Moose::StateArg &)>;
      74             :   using ElemPointFn = std::function<T(const Moose::ElemPointArg &, const Moose::StateArg &)>;
      75             :   using NodeFn = std::function<T(const Moose::NodeArg &, const Moose::StateArg &)>;
      76             : 
      77             :   ValueType evaluate(const Moose::ElemArg & elem_arg, const Moose::StateArg & time) const override;
      78             :   ValueType evaluate(const Moose::FaceArg & face, const Moose::StateArg & time) const override;
      79             :   ValueType evaluate(const Moose::ElemQpArg & elem_qp, const Moose::StateArg & time) const override;
      80             :   ValueType evaluate(const Moose::ElemSideQpArg & elem_side_qp,
      81             :                      const Moose::StateArg & time) const override;
      82             :   ValueType evaluate(const Moose::ElemPointArg & elem_point,
      83             :                      const Moose::StateArg & time) const override;
      84             :   ValueType evaluate(const Moose::NodeArg & node_arg, const Moose::StateArg & time) const override;
      85             : 
      86             :   using Moose::FunctorBase<T>::evaluateGradient;
      87             :   GradientType evaluateGradient(const Moose::ElemArg & elem_arg,
      88             :                                 const Moose::StateArg &) const override;
      89             :   GradientType evaluateGradient(const Moose::FaceArg & face_arg,
      90             :                                 const Moose::StateArg &) const override;
      91             : 
      92             : private:
      93             :   /**
      94             :    * Provide a useful error message about lack of functor material property on the provided
      95             :    * subdomain \p sub_id
      96             :    * @param sub_id subdomain id on which the functor was missing
      97             :    * @param functors map of functors, used to show list of blocks with a definition
      98             :    */
      99             :   template <typename C>
     100             :   void subdomainErrorMessage(SubdomainID sub_id,
     101             :                              const std::unordered_map<SubdomainID, C> & functors) const;
     102             : 
     103             :   /// Functors that return element average values (or cell centroid values or whatever the
     104             :   /// implementer wants to return for a given element argument)
     105             :   std::unordered_map<SubdomainID, ElemFn> _elem_functor;
     106             : 
     107             :   /// Functors that return the property value on the requested side of the face (e.g. the
     108             :   /// infinitesimal + or - side of the face)
     109             :   std::unordered_map<SubdomainID, FaceFn> _face_functor;
     110             : 
     111             :   /// Functors that will evaluate elements at quadrature points
     112             :   std::unordered_map<SubdomainID, ElemQpFn> _elem_qp_functor;
     113             : 
     114             :   /// Functors that will evaluate elements at side quadrature points
     115             :   std::unordered_map<SubdomainID, ElemSideQpFn> _elem_side_qp_functor;
     116             : 
     117             :   /// Functors that return evaluations at an arbitrary physical point in an element
     118             :   std::unordered_map<SubdomainID, ElemPointFn> _elem_point_functor;
     119             : 
     120             :   /// Functors that return nodal values
     121             :   std::unordered_map<SubdomainID, NodeFn> _node_functor;
     122             : 
     123             :   /// The mesh that this functor operates on
     124             :   const MooseMesh & _mesh;
     125             : };
     126             : 
     127             : template <typename T>
     128             : template <typename PolymorphicLambda>
     129        8437 : PiecewiseByBlockLambdaFunctor<T>::PiecewiseByBlockLambdaFunctor(
     130             :     const std::string & name,
     131             :     PolymorphicLambda my_lammy,
     132             :     const std::set<ExecFlagType> & clearance_schedule,
     133             :     const MooseMesh & mesh,
     134             :     const std::set<SubdomainID> & block_ids)
     135        8437 :   : Moose::FunctorBase<T>(name, clearance_schedule), _mesh(mesh)
     136             : {
     137        8437 :   setFunctor(mesh, block_ids, my_lammy);
     138        8437 : }
     139             : 
     140             : template <typename T>
     141             : template <typename PolymorphicLambda>
     142             : void
     143        9426 : PiecewiseByBlockLambdaFunctor<T>::setFunctor(const MooseMesh & mesh,
     144             :                                              const std::set<SubdomainID> & block_ids,
     145             :                                              PolymorphicLambda my_lammy)
     146             : {
     147             :   mooseAssert(&mesh == &_mesh,
     148             :               "We should always be setting this functor with the same mesh. We may relax this "
     149             :               "assertion later");
     150             : 
     151       40415 :   auto add_lammy = [this, my_lammy](const SubdomainID block_id)
     152             :   {
     153       30993 :     auto pr = _elem_functor.emplace(block_id, my_lammy);
     154       30993 :     if (!pr.second)
     155           4 :       mooseError("No insertion for the functor material property '",
     156             :                  this->functorName(),
     157             :                  "' for block id ",
     158             :                  block_id,
     159             :                  ". Another material must already declare this property on that block.");
     160       30989 :     _face_functor.emplace(block_id, my_lammy);
     161       30989 :     _elem_qp_functor.emplace(block_id, my_lammy);
     162       30989 :     _elem_side_qp_functor.emplace(block_id, my_lammy);
     163       30989 :     _elem_point_functor.emplace(block_id, my_lammy);
     164       30989 :     _node_functor.emplace(block_id, my_lammy);
     165             :   };
     166             : 
     167       35179 :   for (const auto block_id : block_ids)
     168       25757 :     add_lammy(block_id);
     169             : 
     170             :   // Handle special case of ANY_BLOCK_ID and empty block restriction that also cover
     171             :   // INVALID_BLOCK_ID
     172       18843 :   if (block_ids.count(Moose::ANY_BLOCK_ID) || block_ids.empty() ||
     173        9421 :       block_ids == mesh.meshSubdomains())
     174        5236 :     add_lammy(Moose::INVALID_BLOCK_ID);
     175        9422 : }
     176             : 
     177             : template <typename T>
     178             : bool
     179         844 : PiecewiseByBlockLambdaFunctor<T>::isExtrapolatedBoundaryFace(const FaceInfo & fi,
     180             :                                                              const Elem *,
     181             :                                                              const Moose::StateArg &) const
     182             : {
     183         844 :   if (!fi.neighborPtr())
     184         424 :     return true;
     185             : 
     186         420 :   const bool defined_on_elem = _elem_functor.count(fi.elem().subdomain_id());
     187         420 :   const bool defined_on_neighbor = _elem_functor.count(fi.neighbor().subdomain_id());
     188         420 :   const bool extrapolated = (defined_on_elem + defined_on_neighbor) == 1;
     189             : 
     190             :   mooseAssert(defined_on_elem || defined_on_neighbor,
     191             :               "This shouldn't be called if we aren't defined on either side.");
     192         420 :   return extrapolated;
     193             : }
     194             : 
     195             : template <typename T>
     196             : bool
     197       21144 : PiecewiseByBlockLambdaFunctor<T>::hasBlocks(const SubdomainID id) const
     198             : {
     199             :   // If any of the maps has a functor for that block, it has the block
     200       21144 :   const bool has_blocks = _elem_functor.count(id);
     201             :   mooseAssert(has_blocks == _face_functor.count(id),
     202             :               "All functor sets should agree on whether we have this sub id");
     203             :   mooseAssert(has_blocks == _elem_qp_functor.count(id),
     204             :               "All functor sets should agree on whether we have this sub id");
     205             :   mooseAssert(has_blocks == _elem_side_qp_functor.count(id),
     206             :               "All functor sets should agree on whether we have this sub id");
     207       21144 :   return has_blocks;
     208             : }
     209             : 
     210             : template <typename T>
     211             : template <typename C>
     212             : void
     213           6 : PiecewiseByBlockLambdaFunctor<T>::subdomainErrorMessage(
     214             :     const SubdomainID sub_id, const std::unordered_map<SubdomainID, C> & functors) const
     215             : {
     216           6 :   std::vector<SubdomainID> block_ids;
     217           6 :   block_ids.reserve(functors.size());
     218          12 :   for (const auto & [available_sub_id, functor] : functors)
     219             :   {
     220           6 :     libmesh_ignore(functor);
     221           6 :     block_ids.push_back(available_sub_id);
     222             :   }
     223          42 :   mooseError("The provided subdomain ID ",
     224             :              std::to_string(sub_id),
     225             :              " doesn't exist in the map for lambda functor '",
     226             :              this->functorName(),
     227             :              "'! This is likely because you did not provide a functor material "
     228             :              "definition on that subdomain.\nSubdomain IDs in the map: ",
     229             :              Moose::stringify(block_ids));
     230           6 : }
     231             : 
     232             : template <typename T>
     233             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     234     5164917 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::ElemArg & elem_arg,
     235             :                                            const Moose::StateArg & time) const
     236             : {
     237     5164917 :   const Elem * const elem = elem_arg.elem;
     238             :   mooseAssert(elem && elem != libMesh::remote_elem,
     239             :               "The element must be non-null and non-remote in functor material properties");
     240     5164917 :   auto it = _elem_functor.find(elem->subdomain_id());
     241     5164917 :   if (it == _elem_functor.end())
     242           1 :     subdomainErrorMessage(elem->subdomain_id(), _elem_functor);
     243             : 
     244    10329832 :   return it->second(elem_arg, time);
     245             : }
     246             : 
     247             : template <typename T>
     248             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     249      645604 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::FaceArg & face,
     250             :                                            const Moose::StateArg & time) const
     251             : {
     252             :   using namespace Moose::FV;
     253             : 
     254      645604 :   if (face.face_side)
     255             :   {
     256      193577 :     const auto sub_id = face.face_side->subdomain_id();
     257      193577 :     auto it = _face_functor.find(sub_id);
     258      193577 :     if (it == _face_functor.end())
     259           1 :       subdomainErrorMessage(sub_id, _face_functor);
     260             : 
     261      193576 :     return it->second(face, time);
     262             :   }
     263             : 
     264             :   mooseAssert(this->isInternalFace(*face.fi),
     265             :               "If we did not have a face side, then we must be an internal face");
     266      452027 :   return interpolate(*this, face, time);
     267             : }
     268             : 
     269             : template <typename T>
     270             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     271     7702587 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::ElemQpArg & elem_qp,
     272             :                                            const Moose::StateArg & time) const
     273             : {
     274     7702587 :   const auto sub_id = elem_qp.elem->subdomain_id();
     275     7702587 :   auto it = _elem_qp_functor.find(sub_id);
     276     7702587 :   if (it == _elem_qp_functor.end())
     277           1 :     subdomainErrorMessage(sub_id, _elem_qp_functor);
     278             : 
     279    15405172 :   return it->second(elem_qp, time);
     280             : }
     281             : 
     282             : template <typename T>
     283             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     284       89873 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::ElemSideQpArg & elem_side_qp,
     285             :                                            const Moose::StateArg & time) const
     286             : {
     287       89873 :   const auto sub_id = elem_side_qp.elem->subdomain_id();
     288       89873 :   auto it = _elem_side_qp_functor.find(sub_id);
     289       89873 :   if (it == _elem_side_qp_functor.end())
     290           1 :     subdomainErrorMessage(sub_id, _elem_side_qp_functor);
     291             : 
     292      179744 :   return it->second(elem_side_qp, time);
     293             : }
     294             : 
     295             : template <typename T>
     296             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     297           2 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::ElemPointArg & elem_point_arg,
     298             :                                            const Moose::StateArg & time) const
     299             : {
     300           2 :   const Elem * const elem = elem_point_arg.elem;
     301             :   mooseAssert(elem && elem != libMesh::remote_elem,
     302             :               "The element must be non-null and non-remote in functor material properties");
     303           2 :   auto it = _elem_point_functor.find(elem->subdomain_id());
     304           2 :   if (it == _elem_point_functor.end())
     305           1 :     subdomainErrorMessage(elem->subdomain_id(), _elem_point_functor);
     306             : 
     307           2 :   return it->second(elem_point_arg, time);
     308             : }
     309             : 
     310             : template <typename T>
     311             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     312          15 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::NodeArg & node_arg,
     313             :                                            const Moose::StateArg & time) const
     314             : {
     315             :   mooseAssert(node_arg.node, "The node must be non-null in functor material properties");
     316          15 :   if (node_arg.subdomain_ids->size() != 1)
     317           0 :     mooseError("We do not currently support multi-subdomain evaluation of nodal arguments");
     318          15 :   const auto sub_id = *(node_arg.subdomain_ids->begin());
     319          15 :   auto it = _node_functor.find(sub_id);
     320          15 :   if (it == _node_functor.end())
     321           1 :     subdomainErrorMessage(sub_id, _node_functor);
     322             : 
     323          28 :   return it->second(node_arg, time);
     324             : }
     325             : 
     326             : template <typename T>
     327             : typename PiecewiseByBlockLambdaFunctor<T>::GradientType
     328         160 : PiecewiseByBlockLambdaFunctor<T>::evaluateGradient(const Moose::ElemArg & elem_arg,
     329             :                                                    const Moose::StateArg & time) const
     330             : {
     331         160 :   return Moose::FV::greenGaussGradient(elem_arg, time, *this, true, _mesh);
     332             : }
     333             : 
     334             : template <typename T>
     335             : typename PiecewiseByBlockLambdaFunctor<T>::GradientType
     336          72 : PiecewiseByBlockLambdaFunctor<T>::evaluateGradient(const Moose::FaceArg & face_arg,
     337             :                                                    const Moose::StateArg & time) const
     338             : {
     339          72 :   return Moose::FV::greenGaussGradient(face_arg, time, *this, true, _mesh);
     340             : }

Generated by: LCOV version 1.14