File indexing completed on 2025-09-18 09:09:41
0001
0002
0003
0004
0005
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
0021 struct IntegratorOptions
0022 {
0023 using DepthInt = short unsigned int;
0024
0025 real_type epsilon{1e-3};
0026 DepthInt max_depth{20};
0027
0028
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
0039
0040
0041
0042
0043
0044
0045
0046 template<class F>
0047 class Integrator
0048 {
0049 public:
0050
0051
0052 using argument_type = real_type;
0053 using result_type = real_type;
0054 using Options = IntegratorOptions;
0055
0056
0057 public:
0058
0059 inline Integrator(F&& func, Options opts);
0060
0061
0062 explicit Integrator(F&& func) : Integrator{std::forward<F>(func), {}} {}
0063
0064
0065 inline result_type operator()(argument_type lo, argument_type hi);
0066
0067 private:
0068 F eval_;
0069 Options options_;
0070
0071
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
0081
0082
0083 template<class F, class... Args>
0084 Integrator(F&&, Args...) -> Integrator<F>;
0085
0086
0087
0088
0089
0090
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
0102
0103 template<class F>
0104 auto Integrator<F>::operator()(argument_type lo, argument_type hi) -> result_type
0105 {
0106 CELER_EXPECT(lo < hi);
0107 constexpr real_type half{0.5};
0108
0109 size_type num_points = 1;
0110 real_type dx = (hi - lo);
0111
0112
0113 real_type result = half * dx * (eval_(lo) + eval_(hi));
0114 real_type prev{};
0115
0116 auto remaining_trials = options_.max_depth;
0117 do
0118 {
0119
0120 real_type accum = 0;
0121 for (auto i : range(num_points))
0122 {
0123 real_type x = std::fma((half + static_cast<real_type>(i)), dx, lo);
0124 accum += eval_(x);
0125 }
0126
0127
0128
0129 prev = result;
0130 result = half * (prev + accum * dx);
0131
0132
0133 num_points *= 2;
0134 dx *= half;
0135 } while (!this->converged(prev, result) && --remaining_trials > 0);
0136
0137 return result;
0138 }
0139
0140
0141 }