Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-21 08:21:47

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file corecel/math/Integrator.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <cmath>
0010 
0011 #include "corecel/Assert.hh"
0012 #include "corecel/Types.hh"
0013 #include "corecel/cont/Range.hh"
0014 
0015 #include "Algorithms.hh"
0016 
0017 namespace celeritas
0018 {
0019 //---------------------------------------------------------------------------//
0020 //! Solver options
0021 struct IntegratorOptions
0022 {
0023     using DepthInt = short unsigned int;
0024 
0025     real_type epsilon{1e-3};  //!< Convergence criterion
0026     DepthInt max_depth{20};  //!< Maximum number of outer iterations
0027 
0028     //! Whether the options are valid
0029     explicit operator bool() const
0030     {
0031         return epsilon > 0 && max_depth > 0
0032                && static_cast<std::size_t>(max_depth) < 8 * sizeof(size_type);
0033     }
0034 };
0035 
0036 //---------------------------------------------------------------------------//
0037 /*!
0038  * Perform numerical integration of a generic 1-D function.
0039  *
0040  * Currently this is a simple nonrecursive Newton-Coates integrator extracted
0041  * from NuclearZoneBuilder. It should be improved for robustness, accuracy, and
0042  * efficiency, probably by using a Gauss-Legendre quadrature.
0043  *
0044  * This class is to be used \em only during setup.
0045  */
0046 template<class F>
0047 class Integrator
0048 {
0049   public:
0050     //!@{
0051     //! \name Type aliases
0052     using argument_type = real_type;
0053     using result_type = real_type;
0054     using Options = IntegratorOptions;
0055     //!@}
0056 
0057   public:
0058     // Construct with the function and options
0059     inline Integrator(F&& func, Options opts);
0060 
0061     //! Construct with the function and default options
0062     explicit Integrator(F&& func) : Integrator{std::forward<F>(func), {}} {}
0063 
0064     // Calculate the integral over the given integral
0065     inline result_type operator()(argument_type lo, argument_type hi);
0066 
0067   private:
0068     F eval_;
0069     Options options_;
0070 
0071     //! Whether convergence is achieved (simple soft equality)
0072     bool converged(real_type prev, real_type cur) const
0073     {
0074         CELER_EXPECT(!std::isinf(cur) && !std::isnan(cur));
0075         return std::fabs(cur - prev) <= options_.epsilon * std::fabs(cur);
0076     }
0077 };
0078 
0079 //---------------------------------------------------------------------------//
0080 // TEMPLATE DEDUCTION
0081 //---------------------------------------------------------------------------//
0082 
0083 template<class F, class... Args>
0084 Integrator(F&&, Args...) -> Integrator<F>;
0085 
0086 //---------------------------------------------------------------------------//
0087 // INLINE DEFINITIONS
0088 //---------------------------------------------------------------------------//
0089 /*!
0090  * Construct with the function and options.
0091  */
0092 template<class F>
0093 Integrator<F>::Integrator(F&& func, Options opts)
0094     : eval_{std::forward<F>(func)}, options_{opts}
0095 {
0096     CELER_EXPECT(options_);
0097 }
0098 
0099 //---------------------------------------------------------------------------//
0100 /*!
0101  * Calculate the integral over the given dx.
0102  */
0103 template<class F>
0104 auto Integrator<F>::operator()(argument_type lo, argument_type hi)
0105     -> result_type
0106 {
0107     CELER_EXPECT(lo < hi);
0108     constexpr real_type half{0.5};
0109 
0110     size_type num_points = 1;
0111     real_type dx = (hi - lo);
0112 
0113     // Initial estimate is the endpoints of the function
0114     real_type result = half * dx * (eval_(lo) + eval_(hi));
0115     real_type prev{};
0116 
0117     auto remaining_trials = options_.max_depth;
0118     do
0119     {
0120         // Accumulate the sum of midpoints along the grid spacing
0121         real_type accum = 0;
0122         for (auto i : range(num_points))
0123         {
0124             real_type x = std::fma((half + static_cast<real_type>(i)), dx, lo);
0125             accum += eval_(x);
0126         }
0127 
0128         // Average previous and new integrations, i.e. combining all the
0129         // existing and current grid points
0130         prev = result;
0131         result = half * (prev + accum * dx);
0132 
0133         // Increase number of intervals (and decrease dx) for next iteration
0134         num_points *= 2;
0135         dx *= half;
0136     } while (!this->converged(prev, result) && --remaining_trials > 0);
0137 
0138     return result;
0139 }
0140 
0141 //---------------------------------------------------------------------------//
0142 }  // namespace celeritas