LCOV - code coverage report
Current view: top level - src/functions - FunctionSeries.C (source / functions) Hit Total Coverage
Test: idaholab/moose functional_expansion_tools: #31405 (292dce) with base fef103 Lines: 102 109 93.6 %
Date: 2025-09-04 07:53:29 Functions: 15 15 100.0 %
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             : #include <numeric> // Provides accumulate()
      11             : 
      12             : #include "FunctionalBasisInterface.h" // Provides _domain_options
      13             : #include "FunctionSeries.h"
      14             : #include "Cartesian.h"
      15             : #include "CylindricalDuo.h"
      16             : 
      17             : registerMooseObject("FunctionalExpansionToolsApp", FunctionSeries);
      18             : 
      19             : InputParameters
      20         659 : FunctionSeries::validParams()
      21             : {
      22         659 :   InputParameters params = MutableCoefficientsFunctionInterface::validParams();
      23             : 
      24         659 :   params.addClassDescription("This function uses a convolution of functional series (functional "
      25             :                              "expansion or FX) to create a 1D, 2D, or 3D function");
      26             : 
      27             :   // The available composite series types.
      28             :   //   Cartesian:      1D, 2D, or 3D, depending on which of x, y, and z are present
      29             :   //   CylindricalDuo: planar disc expansion and axial expansion
      30        1318 :   MooseEnum series_types("Cartesian CylindricalDuo");
      31        1318 :   MooseEnum single_series_types_1D("Legendre");
      32        1318 :   MooseEnum single_series_types_2D("Zernike");
      33             : 
      34        1318 :   params.addRequiredParam<MooseEnum>(
      35             :       "series_type", series_types, "The type of function series to construct.");
      36             : 
      37             :   /*
      38             :    * This needs to use `unsigned int` instead of `std::size_t` because otherwise MOOSE errors at
      39             :    * runtime
      40             :    */
      41        1318 :   params.addRequiredParam<std::vector<unsigned int>>("orders",
      42             :                                                      "The order of each series. These must be "
      43             :                                                      "defined as \"x y z\" for Cartesian, and \"z "
      44             :                                                      "disc\" for CylindricalDuo.");
      45             : 
      46        1318 :   params.addParam<std::vector<Real>>("physical_bounds",
      47             :                                      "The physical bounds of the function series. These must be "
      48             :                                      "defined as \"x_min x_max y_min y_max z_min z_max\" for "
      49             :                                      "Cartesian, and \"axial_min axial_max disc_center1 "
      50             :                                      "disc_center2 radius\" for CylindricalDuo");
      51             : 
      52        1318 :   params.addParam<MooseEnum>("x", single_series_types_1D, "The series to use for the x-direction.");
      53        1318 :   params.addParam<MooseEnum>("y", single_series_types_1D, "The series to use for the y-direction.");
      54        1318 :   params.addParam<MooseEnum>("z", single_series_types_1D, "The series to use for the z-direction.");
      55             : 
      56        1318 :   params.addParam<MooseEnum>("disc",
      57             :                              single_series_types_2D,
      58             :                              "The series to use for the disc. Its direction is determined by "
      59             :                              "orthogonality to the declared direction of the axis.");
      60             : 
      61         659 :   std::string normalization_types = "orthonormal sqrt_mu standard";
      62        1318 :   MooseEnum expansion_type(normalization_types, "standard");
      63        1318 :   MooseEnum generation_type(normalization_types, "orthonormal");
      64        1318 :   params.addParam<MooseEnum>("expansion_type",
      65             :                              expansion_type,
      66             :                              "The normalization used for expansion of the basis functions");
      67        1318 :   params.addParam<MooseEnum>(
      68             :       "generation_type",
      69             :       generation_type,
      70             :       "The normalization used for generation of the basis function coefficients");
      71         659 :   return params;
      72        1318 : }
      73             : 
      74         392 : FunctionSeries::FunctionSeries(const InputParameters & parameters)
      75             :   : MutableCoefficientsFunctionInterface(this, parameters),
      76         392 :     _orders(convertOrders(getParam<std::vector<unsigned int>>("orders"))),
      77        1176 :     _physical_bounds(getParam<std::vector<Real>>("physical_bounds")),
      78         784 :     _series_type_name(getParam<MooseEnum>("series_type")),
      79         784 :     _x(getParam<MooseEnum>("x")),
      80         784 :     _y(getParam<MooseEnum>("y")),
      81         784 :     _z(getParam<MooseEnum>("z")),
      82         784 :     _disc(getParam<MooseEnum>("disc")),
      83         784 :     _expansion_type(getParam<MooseEnum>("expansion_type")),
      84        1176 :     _generation_type(getParam<MooseEnum>("generation_type"))
      85             : {
      86             :   std::vector<MooseEnum> domains;
      87             :   std::vector<MooseEnum> types;
      88             : 
      89         392 :   if (_series_type_name == "Cartesian")
      90             :   {
      91             :     /*
      92             :      * For Cartesian series, at least one of 'x', 'y', and 'z' must be specified.
      93             :      *
      94             :      * The individual series are always stored in x, y, z order (independent of the order in which
      95             :      * they appear in the input file). Hence, the 'orders' and 'physical_bounds' vectors must always
      96             :      * be specified in x, y, z order.
      97             :      */
      98         772 :     if (isParamValid("x"))
      99             :     {
     100         232 :       domains.push_back(FunctionalBasisInterface::_domain_options = "x");
     101         232 :       types.push_back(_x);
     102             :     }
     103         772 :     if (isParamValid("y"))
     104             :     {
     105         152 :       domains.push_back(FunctionalBasisInterface::_domain_options = "y");
     106         152 :       types.push_back(_y);
     107             :     }
     108         772 :     if (isParamValid("z"))
     109             :     {
     110           0 :       domains.push_back(FunctionalBasisInterface::_domain_options = "z");
     111           0 :       types.push_back(_z);
     112             :     }
     113         386 :     if (types.size() == 0)
     114           2 :       mooseError("Must specify one of 'x', 'y', or 'z' for 'Cartesian' series!");
     115         766 :     _series_type = std::make_unique<Cartesian>(
     116         384 :         domains, _orders, types, name(), _expansion_type, _generation_type);
     117             :   }
     118           6 :   else if (_series_type_name == "CylindricalDuo")
     119             :   {
     120             :     /*
     121             :      * CylindricalDuo represents a disc-axial expansion, where the disc is described by a single
     122             :      * series, such as Zernike (as opposed to a series individually representing r and a second
     123             :      * series independently representing theta. For CylindricalDuo series, the series are always
     124             :      * stored in the axial, planar order, independent of which order the series appear in the input
     125             :      * file. Therefore, the _orders and _physical_bounds vectors must always appear in axial, planar
     126             :      * order. The first entry in _domains is interpreted as the axial direction, and the following
     127             :      * two as the planar.
     128             :      */
     129          12 :     if (isParamValid("x"))
     130             :     {
     131          20 :       domains = {FunctionalBasisInterface::_domain_options = "x",
     132           8 :                  FunctionalBasisInterface::_domain_options = "y",
     133          24 :                  FunctionalBasisInterface::_domain_options = "z"};
     134           4 :       types.push_back(_x);
     135             :     }
     136          12 :     if (isParamValid("y"))
     137             :     {
     138          10 :       domains = {FunctionalBasisInterface::_domain_options = "y",
     139           4 :                  FunctionalBasisInterface::_domain_options = "x",
     140          12 :                  FunctionalBasisInterface::_domain_options = "z"};
     141           2 :       types.push_back(_y);
     142             :     }
     143          12 :     if (isParamValid("z"))
     144             :     {
     145           0 :       domains = {FunctionalBasisInterface::_domain_options = "z",
     146           0 :                  FunctionalBasisInterface::_domain_options = "x",
     147           0 :                  FunctionalBasisInterface::_domain_options = "y"};
     148           0 :       types.push_back(_z);
     149             :     }
     150             : 
     151           6 :     if (types.size() == 0)
     152           2 :       mooseError("Must specify one of 'x', 'y', or 'z' for 'CylindricalDuo' series!");
     153             : 
     154           4 :     if (types.size() > 1)
     155           2 :       mooseError("Cannot specify more than one of 'x', 'y', or 'z' for 'CylindricalDuo' series!");
     156             : 
     157           2 :     types.push_back(_disc);
     158           2 :     _series_type = std::make_unique<CylindricalDuo>(
     159           2 :         domains, _orders, types, name(), _expansion_type, _generation_type);
     160             :   }
     161             :   else
     162           0 :     mooseError("Unknown functional series type \"", _series_type_name, "\"");
     163             : 
     164             :   // Set the physical bounds of each of the single series if defined
     165         764 :   if (isParamValid("physical_bounds"))
     166         382 :     _series_type->setPhysicalBounds(_physical_bounds);
     167             : 
     168             :   // Resize the coefficient array as needed
     169         380 :   enforceSize(false), resize(getNumberOfTerms(), 0.0), enforceSize(true);
     170         380 :   setCharacteristics(_orders);
     171         398 : }
     172             : 
     173             : FunctionSeries &
     174         629 : FunctionSeries::checkAndConvertFunction(const Function & function,
     175             :                                         const std::string & typeName,
     176             :                                         const std::string & objectName)
     177             : {
     178         629 :   const FunctionSeries * test = dynamic_cast<const FunctionSeries *>(&function);
     179         629 :   if (!test)
     180           8 :     ::mooseError("In ",
     181             :                  typeName,
     182             :                  "-type object \"",
     183             :                  objectName,
     184             :                  "\": the named Function \"",
     185             :                  function.name(),
     186             :                  "\" must be a FunctionSeries-type object.");
     187             : 
     188         621 :   return *const_cast<FunctionSeries *>(test);
     189             : }
     190             : 
     191             : Real
     192         320 : FunctionSeries::getStandardizedFunctionVolume() const
     193             : {
     194         320 :   return _series_type->getStandardizedFunctionVolume();
     195             : }
     196             : 
     197             : std::size_t
     198        2646 : FunctionSeries::getNumberOfTerms() const
     199             : {
     200        2646 :   return _series_type->getNumberOfTerms();
     201             : }
     202             : 
     203             : const std::vector<size_t> &
     204         320 : FunctionSeries::getOrders() const
     205             : {
     206         320 :   return _orders;
     207             : }
     208             : 
     209             : /*
     210             :  * getAllGeneration() is defined in the FunctionalBasisInterface, which calls the pure virtual
     211             :  * evaluateGeneration() method of the CompositeSeriesBasisInterface class, which then calls the
     212             :  * getAllGeneration() method of each of the single series.
     213             :  */
     214             : const std::vector<Real> &
     215      281008 : FunctionSeries::getGeneration()
     216             : {
     217      281008 :   return _series_type->getAllGeneration();
     218             : }
     219             : 
     220             : /*
     221             :  * getAllExpansion() is defined in the FunctionalBasisInterface, which calls the pure virtual
     222             :  * evaluateExpansion() method of the CompositeSeriesBasisInterface class, which then calls the
     223             :  * getAllExpansion() method of each of the single series.
     224             :  */
     225             : const std::vector<Real> &
     226      160128 : FunctionSeries::getExpansion()
     227             : {
     228      160128 :   return _series_type->getAllExpansion();
     229             : }
     230             : 
     231             : /*
     232             :  * isInPhysicalBounds() is a pure virtual method of the FunctionalBasisInterface that is defined in
     233             :  * the CompositeSeriesBasisInterface class because it is agnostic to the underlying types of the
     234             :  * single series.
     235             :  */
     236             : bool
     237      307070 : FunctionSeries::isInPhysicalBounds(const Point & point) const
     238             : {
     239      307070 :   return _series_type->isInPhysicalBounds(point);
     240             : }
     241             : 
     242             : void
     243      441136 : FunctionSeries::setLocation(const Point & point)
     244             : {
     245      441136 :   _series_type->setLocation(point);
     246      441136 : }
     247             : 
     248             : Real
     249      164420 : FunctionSeries::evaluateValue(Real, const Point & point)
     250             : {
     251             :   // Check that the point is within the physical bounds of the series
     252      164420 :   if (!isInPhysicalBounds(point))
     253             :     return 0.0;
     254             : 
     255             :   // Set the location at which to evaluate the series
     256      160128 :   setLocation(point);
     257             : 
     258      160128 :   return expand();
     259             : }
     260             : 
     261             : Real
     262      160128 : FunctionSeries::expand()
     263             : {
     264      160128 :   return expand(_coefficients);
     265             : }
     266             : 
     267             : Real
     268      160128 : FunctionSeries::expand(const std::vector<Real> & coefficients)
     269             : {
     270             :   // Evaluate all of the terms in the series
     271      160128 :   const std::vector<Real> & terms = getExpansion();
     272             : 
     273      160128 :   return std::inner_product(terms.begin(), terms.end(), coefficients.begin(), 0.0);
     274             : }
     275             : 
     276             : std::ostream &
     277        1626 : operator<<(std::ostream & stream, const FunctionSeries & me)
     278             : {
     279             :   stream << "\n\n"
     280             :          << "FunctionSeries: " << me.name() << "\n"
     281        4878 :          << "         Terms: " << me.getNumberOfTerms() << "\n";
     282        1626 :   me._series_type->formatCoefficients(stream, me._coefficients);
     283        1626 :   stream << "\n\n" << std::flush;
     284             : 
     285        1626 :   return stream;
     286             : }
     287             : 
     288             : std::vector<std::size_t>
     289         392 : FunctionSeries::convertOrders(const std::vector<unsigned int> & orders)
     290             : {
     291         392 :   return std::vector<std::size_t>(orders.begin(), orders.end());
     292             : }

Generated by: LCOV version 1.14