LCOV - code coverage report
Current view: top level - include/utils - PiecewiseByBlockLambdaFunctor.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: d5e406 Lines: 87 90 96.7 %
Date: 2026-07-08 21:15:44 Functions: 229 366 62.6 %
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       14888 :   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             :   /**
     104             :    * Format a subdomain ID with its name, when available.
     105             :    */
     106             :   std::string subdomainNameAndID(SubdomainID sub_id) const;
     107             : 
     108             :   /// Functors that return element average values (or cell centroid values or whatever the
     109             :   /// implementer wants to return for a given element argument)
     110             :   std::unordered_map<SubdomainID, ElemFn> _elem_functor;
     111             : 
     112             :   /// Functors that return the property value on the requested side of the face (e.g. the
     113             :   /// infinitesimal + or - side of the face)
     114             :   std::unordered_map<SubdomainID, FaceFn> _face_functor;
     115             : 
     116             :   /// Functors that will evaluate elements at quadrature points
     117             :   std::unordered_map<SubdomainID, ElemQpFn> _elem_qp_functor;
     118             : 
     119             :   /// Functors that will evaluate elements at side quadrature points
     120             :   std::unordered_map<SubdomainID, ElemSideQpFn> _elem_side_qp_functor;
     121             : 
     122             :   /// Functors that return evaluations at an arbitrary physical point in an element
     123             :   std::unordered_map<SubdomainID, ElemPointFn> _elem_point_functor;
     124             : 
     125             :   /// Functors that return nodal values
     126             :   std::unordered_map<SubdomainID, NodeFn> _node_functor;
     127             : 
     128             :   /// The mesh that this functor operates on
     129             :   const MooseMesh & _mesh;
     130             : };
     131             : 
     132             : template <typename T>
     133             : template <typename PolymorphicLambda>
     134        7773 : PiecewiseByBlockLambdaFunctor<T>::PiecewiseByBlockLambdaFunctor(
     135             :     const std::string & name,
     136             :     PolymorphicLambda my_lammy,
     137             :     const std::set<ExecFlagType> & clearance_schedule,
     138             :     const MooseMesh & mesh,
     139             :     const std::set<SubdomainID> & block_ids)
     140        7773 :   : Moose::FunctorBase<T>(name, clearance_schedule), _mesh(mesh)
     141             : {
     142        7773 :   setFunctor(mesh, block_ids, my_lammy);
     143        7773 : }
     144             : 
     145             : template <typename T>
     146             : template <typename PolymorphicLambda>
     147             : void
     148        8600 : PiecewiseByBlockLambdaFunctor<T>::setFunctor(const MooseMesh & mesh,
     149             :                                              const std::set<SubdomainID> & block_ids,
     150             :                                              PolymorphicLambda my_lammy)
     151             : {
     152             :   mooseAssert(&mesh == &_mesh,
     153             :               "We should always be setting this functor with the same mesh. We may relax this "
     154             :               "assertion later");
     155             : 
     156       37471 :   auto add_lammy = [this, my_lammy](const SubdomainID block_id)
     157             :   {
     158       28874 :     auto pr = _elem_functor.emplace(block_id, my_lammy);
     159       28874 :     if (!pr.second)
     160           3 :       mooseError("No insertion for the functor material property '",
     161             :                  this->functorName(),
     162             :                  "' for block id ",
     163             :                  block_id,
     164             :                  ". Another material must already declare this property on that block.");
     165       28871 :     _face_functor.emplace(block_id, my_lammy);
     166       28871 :     _elem_qp_functor.emplace(block_id, my_lammy);
     167       28871 :     _elem_side_qp_functor.emplace(block_id, my_lammy);
     168       28871 :     _elem_point_functor.emplace(block_id, my_lammy);
     169       28871 :     _node_functor.emplace(block_id, my_lammy);
     170             :   };
     171             : 
     172       32202 :   for (const auto block_id : block_ids)
     173       23605 :     add_lammy(block_id);
     174             : 
     175             :   // Handle special case of ANY_BLOCK_ID and empty block restriction that also cover
     176             :   // INVALID_BLOCK_ID
     177       17192 :   if (block_ids.count(Moose::ANY_BLOCK_ID) || block_ids.empty() ||
     178        8595 :       block_ids == mesh.meshSubdomains())
     179        5269 :     add_lammy(Moose::INVALID_BLOCK_ID);
     180        8597 : }
     181             : 
     182             : template <typename T>
     183             : bool
     184        1688 : PiecewiseByBlockLambdaFunctor<T>::isExtrapolatedBoundaryFace(const FaceInfo & fi,
     185             :                                                              const Elem *,
     186             :                                                              const Moose::StateArg &) const
     187             : {
     188        1688 :   if (!fi.neighborPtr())
     189         848 :     return true;
     190             : 
     191         840 :   const bool defined_on_elem = _elem_functor.count(fi.elem().subdomain_id());
     192         840 :   const bool defined_on_neighbor = _elem_functor.count(fi.neighbor().subdomain_id());
     193         840 :   const bool extrapolated = (defined_on_elem + defined_on_neighbor) == 1;
     194             : 
     195             :   mooseAssert(defined_on_elem || defined_on_neighbor,
     196             :               "This shouldn't be called if we aren't defined on either side.");
     197         840 :   return extrapolated;
     198             : }
     199             : 
     200             : template <typename T>
     201             : bool
     202       20064 : PiecewiseByBlockLambdaFunctor<T>::hasBlocks(const SubdomainID id) const
     203             : {
     204             :   // If any of the maps has a functor for that block, it has the block
     205       20064 :   const bool has_blocks = _elem_functor.count(id);
     206             :   mooseAssert(has_blocks == _face_functor.count(id),
     207             :               "All functor sets should agree on whether we have this sub id");
     208             :   mooseAssert(has_blocks == _elem_qp_functor.count(id),
     209             :               "All functor sets should agree on whether we have this sub id");
     210             :   mooseAssert(has_blocks == _elem_side_qp_functor.count(id),
     211             :               "All functor sets should agree on whether we have this sub id");
     212       20064 :   return has_blocks;
     213             : }
     214             : 
     215             : template <typename T>
     216             : std::string
     217          30 : PiecewiseByBlockLambdaFunctor<T>::subdomainNameAndID(const SubdomainID sub_id) const
     218             : {
     219          30 :   const auto & name = _mesh.getSubdomainName(sub_id);
     220          30 :   if (name.empty())
     221          24 :     return Moose::stringify(sub_id);
     222           6 :   return "'" + name + "' (" + Moose::stringify(sub_id) + ")";
     223             : }
     224             : 
     225             : template <typename T>
     226             : template <typename C>
     227             : void
     228          15 : PiecewiseByBlockLambdaFunctor<T>::subdomainErrorMessage(
     229             :     const SubdomainID sub_id, const std::unordered_map<SubdomainID, C> & functors) const
     230             : {
     231          15 :   std::vector<std::string> block_names_and_ids;
     232          15 :   block_names_and_ids.reserve(functors.size());
     233          30 :   for (const auto & [available_sub_id, functor] : functors)
     234             :   {
     235          15 :     libmesh_ignore(functor);
     236          15 :     block_names_and_ids.push_back(subdomainNameAndID(available_sub_id));
     237             :   }
     238          78 :   mooseError("The provided subdomain ",
     239             :              subdomainNameAndID(sub_id),
     240             :              " doesn't exist in the map for lambda functor '",
     241             :              this->functorName(),
     242             :              "'! This is likely because you did not provide a functor material "
     243             :              "definition on that subdomain.\nSubdomains in the map: ",
     244             :              Moose::stringify(block_names_and_ids));
     245          12 : }
     246             : 
     247             : template <typename T>
     248             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     249     3846468 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::ElemArg & elem_arg,
     250             :                                            const Moose::StateArg & time) const
     251             : {
     252     3846468 :   const Elem * const elem = elem_arg.elem;
     253             :   mooseAssert(elem && elem != libMesh::remote_elem,
     254             :               "The element must be non-null and non-remote in functor material properties");
     255     3846468 :   auto it = _elem_functor.find(elem->subdomain_id());
     256     3846468 :   if (it == _elem_functor.end())
     257           2 :     subdomainErrorMessage(elem->subdomain_id(), _elem_functor);
     258             : 
     259     7692932 :   return it->second(elem_arg, time);
     260             : }
     261             : 
     262             : template <typename T>
     263             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     264      395559 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::FaceArg & face,
     265             :                                            const Moose::StateArg & time) const
     266             : {
     267             :   using namespace Moose::FV;
     268             : 
     269      395559 :   if (face.face_side)
     270             :   {
     271      151305 :     const auto sub_id = face.face_side->subdomain_id();
     272      151305 :     auto it = _face_functor.find(sub_id);
     273      151305 :     if (it == _face_functor.end())
     274           2 :       subdomainErrorMessage(sub_id, _face_functor);
     275             : 
     276      151303 :     return it->second(face, time);
     277             :   }
     278             : 
     279             :   mooseAssert(this->isInternalFace(*face.fi),
     280             :               "If we did not have a face side, then we must be an internal face");
     281      244254 :   return interpolate(*this, face, time);
     282             : }
     283             : 
     284             : template <typename T>
     285             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     286     9077001 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::ElemQpArg & elem_qp,
     287             :                                            const Moose::StateArg & time) const
     288             : {
     289     9077001 :   const auto sub_id = elem_qp.elem->subdomain_id();
     290     9077001 :   auto it = _elem_qp_functor.find(sub_id);
     291     9077001 :   if (it == _elem_qp_functor.end())
     292           5 :     subdomainErrorMessage(sub_id, _elem_qp_functor);
     293             : 
     294    18153992 :   return it->second(elem_qp, time);
     295             : }
     296             : 
     297             : template <typename T>
     298             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     299       97634 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::ElemSideQpArg & elem_side_qp,
     300             :                                            const Moose::StateArg & time) const
     301             : {
     302       97634 :   const auto sub_id = elem_side_qp.elem->subdomain_id();
     303       97634 :   auto it = _elem_side_qp_functor.find(sub_id);
     304       97634 :   if (it == _elem_side_qp_functor.end())
     305           2 :     subdomainErrorMessage(sub_id, _elem_side_qp_functor);
     306             : 
     307      195264 :   return it->second(elem_side_qp, time);
     308             : }
     309             : 
     310             : template <typename T>
     311             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     312           4 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::ElemPointArg & elem_point_arg,
     313             :                                            const Moose::StateArg & time) const
     314             : {
     315           4 :   const Elem * const elem = elem_point_arg.elem;
     316             :   mooseAssert(elem && elem != libMesh::remote_elem,
     317             :               "The element must be non-null and non-remote in functor material properties");
     318           4 :   auto it = _elem_point_functor.find(elem->subdomain_id());
     319           4 :   if (it == _elem_point_functor.end())
     320           2 :     subdomainErrorMessage(elem->subdomain_id(), _elem_point_functor);
     321             : 
     322           4 :   return it->second(elem_point_arg, time);
     323             : }
     324             : 
     325             : template <typename T>
     326             : typename PiecewiseByBlockLambdaFunctor<T>::ValueType
     327          14 : PiecewiseByBlockLambdaFunctor<T>::evaluate(const Moose::NodeArg & node_arg,
     328             :                                            const Moose::StateArg & time) const
     329             : {
     330             :   mooseAssert(node_arg.node, "The node must be non-null in functor material properties");
     331          14 :   if (node_arg.subdomain_ids->size() != 1)
     332           0 :     mooseError("We do not currently support multi-subdomain evaluation of nodal arguments");
     333          14 :   const auto sub_id = *(node_arg.subdomain_ids->begin());
     334          14 :   auto it = _node_functor.find(sub_id);
     335          14 :   if (it == _node_functor.end())
     336           2 :     subdomainErrorMessage(sub_id, _node_functor);
     337             : 
     338          24 :   return it->second(node_arg, time);
     339             : }
     340             : 
     341             : template <typename T>
     342             : typename PiecewiseByBlockLambdaFunctor<T>::GradientType
     343         320 : PiecewiseByBlockLambdaFunctor<T>::evaluateGradient(const Moose::ElemArg & elem_arg,
     344             :                                                    const Moose::StateArg & time) const
     345             : {
     346         320 :   return Moose::FV::greenGaussGradient(elem_arg, time, *this, true, _mesh);
     347             : }
     348             : 
     349             : template <typename T>
     350             : typename PiecewiseByBlockLambdaFunctor<T>::GradientType
     351         144 : PiecewiseByBlockLambdaFunctor<T>::evaluateGradient(const Moose::FaceArg & face_arg,
     352             :                                                    const Moose::StateArg & time) const
     353             : {
     354         144 :   return Moose::FV::greenGaussGradient(face_arg, time, *this, true, _mesh);
     355             : }

Generated by: LCOV version 1.14