File indexing completed on 2025-01-18 09:54:49
0001
0002
0003
0004
0005
0006
0007
0008 #pragma once
0009
0010 #include <cmath>
0011
0012 #include "corecel/Assert.hh"
0013 #include "corecel/Types.hh"
0014 #include "corecel/cont/Range.hh"
0015
0016 #include "Algorithms.hh"
0017
0018 namespace celeritas
0019 {
0020
0021
0022 struct IntegratorOptions
0023 {
0024 using DepthInt = short unsigned int;
0025
0026 real_type epsilon{1e-3};
0027 DepthInt max_depth{20};
0028
0029
0030 explicit operator bool() const
0031 {
0032 return epsilon > 0 && max_depth > 0
0033 && static_cast<std::size_t>(max_depth) < 8 * sizeof(size_type);
0034 }
0035 };
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047 template<class F>
0048 class Integrator
0049 {
0050 public:
0051
0052
0053 using argument_type = real_type;
0054 using result_type = real_type;
0055 using Options = IntegratorOptions;
0056
0057
0058 public:
0059
0060 inline Integrator(F&& func, Options opts);
0061
0062
0063 explicit Integrator(F&& func) : Integrator{std::forward<F>(func), {}} {}
0064
0065
0066 inline result_type operator()(argument_type lo, argument_type hi);
0067
0068 private:
0069 F eval_;
0070 Options options_;
0071
0072
0073 bool converged(real_type prev, real_type cur) const
0074 {
0075 CELER_EXPECT(!std::isinf(cur) && !std::isnan(cur));
0076 return std::fabs(cur - prev) <= options_.epsilon * std::fabs(cur);
0077 }
0078 };
0079
0080
0081
0082
0083
0084 template<class F, class... Args>
0085 Integrator(F&&, Args...) -> Integrator<F>;
0086
0087
0088
0089
0090
0091
0092
0093 template<class F>
0094 Integrator<F>::Integrator(F&& func, Options opts)
0095 : eval_{std::forward<F>(func)}, options_{opts}
0096 {
0097 CELER_EXPECT(options_);
0098 }
0099
0100
0101
0102
0103
0104 template<class F>
0105 auto Integrator<F>::operator()(argument_type lo, argument_type hi) -> 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
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
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
0129
0130 prev = result;
0131 result = half * (prev + accum * dx);
0132
0133
0134 num_points *= 2;
0135 dx *= half;
0136 } while (!this->converged(prev, result) && --remaining_trials > 0);
0137
0138 return result;
0139 }
0140
0141
0142 }