LCOV - code coverage report
Current view: top level - src/utils - LinearInterpolation.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 100 144 69.4 %
Date: 2026-05-29 20:35:17 Functions: 9 17 52.9 %
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 "LinearInterpolation.h"
      11             : #include "MooseUtils.h"
      12             : 
      13             : #include "ChainedReal.h"
      14             : 
      15             : #include <cassert>
      16             : #include <fstream>
      17             : #include <stdexcept>
      18             : 
      19        3496 : LinearInterpolation::LinearInterpolation(const std::vector<Real> & x,
      20             :                                          const std::vector<Real> & y,
      21        3496 :                                          const bool extrap)
      22        3496 :   : _x(x), _y(y), _extrap(extrap)
      23             : {
      24        3496 :   errorCheck();
      25        3505 : }
      26             : 
      27             : void
      28        3596 : LinearInterpolation::errorCheck()
      29             : {
      30        3596 :   if (_x.size() != _y.size())
      31           6 :     throw std::domain_error("Vectors are not the same length");
      32             : 
      33      141225 :   for (unsigned int i = 0; i + 1 < _x.size(); ++i)
      34      137638 :     if (_x[i] >= _x[i + 1])
      35             :     {
      36           3 :       std::ostringstream oss;
      37           3 :       oss << "x-values are not strictly increasing: x[" << i << "]: " << _x[i] << " x[" << i + 1
      38           3 :           << "]: " << _x[i + 1];
      39           3 :       throw std::domain_error(oss.str());
      40           3 :     }
      41        3587 : }
      42             : 
      43             : template <typename T>
      44             : T
      45      679749 : LinearInterpolation::sample(const T & x) const
      46             : {
      47             :   // this is a hard error
      48      679749 :   if (_x.empty())
      49           0 :     mooseError("Trying to evaluate an empty LinearInterpolation");
      50             : 
      51             :   // special case for single function nodes
      52      679749 :   if (_x.size() == 1)
      53           0 :     return _y[0];
      54             : 
      55             :   // endpoint cases
      56      679749 :   if (_extrap)
      57             :   {
      58        1200 :     if (x < _x[0])
      59         776 :       return _y[0] + (x - _x[0]) / (_x[1] - _x[0]) * (_y[1] - _y[0]);
      60         424 :     if (x >= _x.back())
      61         208 :       return _y.back() +
      62         304 :              (x - _x.back()) / (_x[_x.size() - 2] - _x.back()) * (_y[_y.size() - 2] - _y.back());
      63             :   }
      64             :   else
      65             :   {
      66      678549 :     if (x < _x[0])
      67        1390 :       return _y[0];
      68      677159 :     if (x >= _x.back())
      69       20378 :       return _y.back();
      70             :   }
      71             : 
      72      656997 :   auto upper = std::upper_bound(_x.begin(), _x.end(), x);
      73     1313994 :   const auto i = cast_int<std::size_t>(std::distance(_x.begin(), upper) - 1);
      74      656997 :   if (i == cast_int<std::size_t>(_x.size() - 1))
      75             :     // std::upper_bound returns the end() iterator if there are no elements that are
      76             :     // an upper bound to the value. Since x >= _x.back() has already returned above,
      77             :     // this means x is a NaN, so we return a NaN here.
      78           2 :     return std::nan("");
      79             :   else
      80      656995 :     return _y[i] + (_y[i + 1] - _y[i]) * (x - _x[i]) / (_x[i + 1] - _x[i]);
      81             : }
      82             : 
      83             : template Real LinearInterpolation::sample<Real>(const Real &) const;
      84             : template ADReal LinearInterpolation::sample<ADReal>(const ADReal &) const;
      85             : template ChainedReal LinearInterpolation::sample<ChainedReal>(const ChainedReal &) const;
      86             : 
      87             : template <typename T>
      88             : T
      89        4386 : LinearInterpolation::sampleDerivative(const T & x) const
      90             : {
      91             :   // endpoint cases
      92        4386 :   if (_extrap)
      93             :   {
      94         576 :     if (x <= _x[0])
      95         384 :       return (_y[1] - _y[0]) / (_x[1] - _x[0]);
      96         192 :     if (x >= _x.back())
      97          96 :       return (_y[_y.size() - 2] - _y.back()) / (_x[_x.size() - 2] - _x.back());
      98             :   }
      99             :   else
     100             :   {
     101        3810 :     if (x < _x[0])
     102           2 :       return 0.0;
     103        3808 :     if (x >= _x.back())
     104           0 :       return 0.0;
     105             :   }
     106             : 
     107        3904 :   auto upper = std::upper_bound(_x.begin(), _x.end(), x);
     108        7808 :   const auto i = cast_int<std::size_t>(std::distance(_x.begin(), upper) - 1);
     109        3904 :   if (i == cast_int<std::size_t>(_x.size() - 1))
     110             :     // std::upper_bound returns the end() iterator if there are no elements that are
     111             :     // an upper bound to the value. Since x >= _x.back() has already returned above,
     112             :     // this means x is a NaN, so we return a NaN here.
     113           2 :     return std::nan("");
     114             :   else
     115        3902 :     return (_y[i + 1] - _y[i]) / (_x[i + 1] - _x[i]);
     116             : }
     117             : 
     118             : template Real LinearInterpolation::sampleDerivative<Real>(const Real &) const;
     119             : template ADReal LinearInterpolation::sampleDerivative<ADReal>(const ADReal &) const;
     120             : template ChainedReal LinearInterpolation::sampleDerivative<ChainedReal>(const ChainedReal &) const;
     121             : 
     122             : Real
     123           0 : LinearInterpolation::integrate()
     124             : {
     125           0 :   Real answer = 0;
     126           0 :   for (unsigned int i = 1; i < _x.size(); ++i)
     127           0 :     answer += 0.5 * (_y[i] + _y[i - 1]) * (_x[i] - _x[i - 1]);
     128             : 
     129           0 :   return answer;
     130             : }
     131             : 
     132             : template <typename T>
     133             : T
     134          10 : LinearInterpolation::integratePartial(const T & xA, const T & xB) const
     135             : {
     136             :   // integral computation below will assume that x2 > x1; if this is not the
     137             :   // case, compute as if it is and then use identity to convert
     138             :   const T *x1_ptr, *x2_ptr;
     139             :   bool switch_bounds;
     140          10 :   const Real rawA = MetaPhysicL::raw_value(xA), rawB = MetaPhysicL::raw_value(xB);
     141             :   Real raw1, raw2;
     142          10 :   if (MooseUtils::absoluteFuzzyEqual(rawA, rawB))
     143           0 :     return 0.0;
     144          10 :   else if (rawB > rawA)
     145             :   {
     146           8 :     x1_ptr = &xA;
     147           8 :     x2_ptr = &xB;
     148           8 :     raw1 = rawA;
     149           8 :     raw2 = rawB;
     150           8 :     switch_bounds = false;
     151             :   }
     152             :   else
     153             :   {
     154           2 :     x1_ptr = &xB;
     155           2 :     x2_ptr = &xA;
     156           2 :     raw1 = rawB;
     157           2 :     raw2 = rawA;
     158           2 :     switch_bounds = true;
     159             :   }
     160          10 :   const auto & x1 = *x1_ptr;
     161          10 :   const auto & x2 = *x2_ptr;
     162             : 
     163             :   // compute integral with knowledge that x2 > x1
     164          10 :   T integral = 0.0;
     165             :   // find minimum i : x[i] > x; if x > x[n-1], i = n
     166          10 :   const auto n = _x.size();
     167           0 :   const unsigned int i1 =
     168          20 :       raw1 <= _x[n - 1] ? std::distance(_x.begin(), std::upper_bound(_x.begin(), _x.end(), raw1))
     169             :                         : n;
     170           4 :   const unsigned int i2 =
     171          16 :       raw2 <= _x[n - 1] ? std::distance(_x.begin(), std::upper_bound(_x.begin(), _x.end(), raw2))
     172             :                         : n;
     173          10 :   unsigned int i = i1;
     174          44 :   while (i <= i2)
     175             :   {
     176          34 :     if (i == 0)
     177             :     {
     178             :       // note i1 = i
     179           0 :       T integral1, integral2;
     180           4 :       if (_extrap)
     181             :       {
     182           0 :         const auto dydx = (_y[1] - _y[0]) / (_x[1] - _x[0]);
     183           0 :         const auto y1 = _y[0] + dydx * (x1 - _x[0]);
     184           0 :         integral1 = 0.5 * (y1 + _y[0]) * (_x[0] - x1);
     185           0 :         if (i2 == i)
     186             :         {
     187           0 :           const auto y2 = _y[0] + dydx * (x2 - _x[0]);
     188           0 :           integral2 = 0.5 * (y2 + _y[0]) * (_x[0] - x2);
     189           0 :         }
     190             :         else
     191           0 :           integral2 = 0.0;
     192           0 :       }
     193             :       else
     194             :       {
     195           4 :         integral1 = _y[0] * (_x[0] - x1);
     196           4 :         if (i2 == i)
     197           0 :           integral2 = _y[0] * (_x[0] - x2);
     198             :         else
     199           4 :           integral2 = 0.0;
     200             :       }
     201             : 
     202           4 :       integral += integral1 - integral2;
     203           0 :     }
     204          30 :     else if (i == n)
     205             :     {
     206             :       // note i2 = i
     207           0 :       T integral1, integral2;
     208           4 :       if (_extrap)
     209             :       {
     210           0 :         const auto dydx = (_y[n - 1] - _y[n - 2]) / (_x[n - 1] - _x[n - 2]);
     211           0 :         const auto y2 = _y[n - 1] + dydx * (x2 - _x[n - 1]);
     212           0 :         integral2 = 0.5 * (y2 + _y[n - 1]) * (x2 - _x[n - 1]);
     213           0 :         if (i1 == n)
     214             :         {
     215           0 :           const auto y1 = _y[n - 1] + dydx * (x1 - _x[n - 1]);
     216           0 :           integral1 = 0.5 * (y1 + _y[n - 1]) * (x1 - _x[n - 1]);
     217           0 :         }
     218             :         else
     219           0 :           integral1 = 0.0;
     220           0 :       }
     221             :       else
     222             :       {
     223           4 :         integral2 = _y[n - 1] * (x2 - _x[n - 1]);
     224           4 :         if (i1 == n)
     225           0 :           integral1 = _y[n - 1] * (x1 - _x[n - 1]);
     226             :         else
     227           4 :           integral1 = 0.0;
     228             :       }
     229             : 
     230           4 :       integral += integral2 - integral1;
     231           0 :     }
     232             :     else
     233             :     {
     234           0 :       T integral1;
     235          26 :       if (i == i1)
     236             :       {
     237           6 :         const auto dydx = (_y[i] - _y[i - 1]) / (_x[i] - _x[i - 1]);
     238           6 :         const auto y1 = _y[i - 1] + dydx * (x1 - _x[i - 1]);
     239           6 :         integral1 = 0.5 * (y1 + _y[i - 1]) * (x1 - _x[i - 1]);
     240           0 :       }
     241             :       else
     242          20 :         integral1 = 0.0;
     243             : 
     244           0 :       T integral2;
     245          26 :       if (i == i2)
     246             :       {
     247           6 :         const auto dydx = (_y[i] - _y[i - 1]) / (_x[i] - _x[i - 1]);
     248           6 :         const auto y2 = _y[i - 1] + dydx * (x2 - _x[i - 1]);
     249           6 :         integral2 = 0.5 * (y2 + _y[i - 1]) * (x2 - _x[i - 1]);
     250           0 :       }
     251             :       else
     252          20 :         integral2 = 0.5 * (_y[i] + _y[i - 1]) * (_x[i] - _x[i - 1]);
     253             : 
     254          26 :       integral += integral2 - integral1;
     255           0 :     }
     256             : 
     257          34 :     i++;
     258             :   }
     259             : 
     260             :   // apply identity if bounds were switched
     261          10 :   if (switch_bounds)
     262           2 :     return -1.0 * integral;
     263             :   else
     264           8 :     return integral;
     265           0 : }
     266             : 
     267             : template Real LinearInterpolation::integratePartial(const Real &, const Real &) const;
     268             : template ADReal LinearInterpolation::integratePartial(const ADReal &, const ADReal &) const;
     269             : 
     270             : Real
     271           0 : LinearInterpolation::domain(int i) const
     272             : {
     273           0 :   return _x[i];
     274             : }
     275             : 
     276             : Real
     277           0 : LinearInterpolation::range(int i) const
     278             : {
     279           0 :   return _y[i];
     280             : }
     281             : 
     282             : unsigned int
     283         402 : LinearInterpolation::getSampleSize() const
     284             : {
     285         402 :   return _x.size();
     286             : }

Generated by: LCOV version 1.14