File indexing completed on 2025-02-22 10:31:30
0001
0002
0003
0004
0005
0006
0007
0008 #pragma once
0009
0010 #include <cmath>
0011 #include <type_traits>
0012
0013 #include "corecel/Assert.hh"
0014 #include "corecel/Macros.hh"
0015 #include "corecel/Types.hh"
0016 #include "corecel/math/Algorithms.hh"
0017 #include "celeritas/Constants.hh"
0018
0019 #include "GenerateCanonical.hh"
0020
0021 namespace celeritas
0022 {
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036 template<class RealType = ::celeritas::real_type>
0037 class NormalDistribution
0038 {
0039 static_assert(std::is_floating_point_v<RealType>);
0040
0041 public:
0042
0043
0044 using real_type = RealType;
0045 using result_type = real_type;
0046
0047
0048 public:
0049
0050 inline CELER_FUNCTION NormalDistribution(real_type mean, real_type stddev);
0051
0052
0053 explicit CELER_FUNCTION NormalDistribution(real_type mean)
0054 : NormalDistribution{mean, 1}
0055 {
0056 }
0057
0058
0059 CELER_FUNCTION NormalDistribution() : NormalDistribution{0, 1} {}
0060
0061
0062 inline CELER_FUNCTION NormalDistribution(NormalDistribution const& other);
0063
0064
0065 inline CELER_FUNCTION NormalDistribution(NormalDistribution&& other);
0066
0067
0068 inline CELER_FUNCTION NormalDistribution&
0069 operator=(NormalDistribution const&);
0070
0071
0072 inline CELER_FUNCTION NormalDistribution& operator=(NormalDistribution&&);
0073
0074
0075 ~NormalDistribution() = default;
0076
0077
0078 template<class Generator>
0079 inline CELER_FUNCTION result_type operator()(Generator& rng);
0080
0081 private:
0082
0083 real_type mean_;
0084 real_type stddev_;
0085
0086
0087 real_type spare_{};
0088 bool has_spare_{false};
0089 };
0090
0091
0092
0093
0094
0095
0096
0097 template<class RealType>
0098 CELER_FUNCTION
0099 NormalDistribution<RealType>::NormalDistribution(real_type mean,
0100 real_type stddev)
0101 : mean_(mean), stddev_(stddev)
0102 {
0103 CELER_EXPECT(stddev > 0);
0104 }
0105
0106
0107
0108
0109
0110 template<class RealType>
0111 CELER_FUNCTION
0112 NormalDistribution<RealType>::NormalDistribution(NormalDistribution const& other)
0113 : mean_{other.mean}, stddev_{other.stddev}
0114 {
0115 }
0116
0117
0118
0119
0120
0121 template<class RealType>
0122 CELER_FUNCTION
0123 NormalDistribution<RealType>::NormalDistribution(NormalDistribution&& other)
0124 : mean_{other.mean_}
0125 , stddev_{other.stddev_}
0126 , spare_{other.spare_}
0127 , has_spare_{other.has_spare_}
0128 {
0129 other.has_spare_ = false;
0130 }
0131
0132
0133
0134
0135
0136 template<class RealType>
0137 CELER_FUNCTION NormalDistribution<RealType>&
0138 NormalDistribution<RealType>::operator=(NormalDistribution const& other)
0139 {
0140 mean_ = other.mean_;
0141 stddev_ = other.stddev_;
0142 return *this;
0143 }
0144
0145
0146
0147
0148
0149 template<class RealType>
0150 CELER_FUNCTION NormalDistribution<RealType>&
0151 NormalDistribution<RealType>::operator=(NormalDistribution&& other)
0152 {
0153 mean_ = other.mean_;
0154 stddev_ = other.stddev_;
0155 if (!has_spare_ && other.has_spare_)
0156 {
0157 spare_ = other.spare_;
0158 has_spare_ = other.has_spare_;
0159 other.has_spare_ = false;
0160 }
0161 return *this;
0162 }
0163
0164
0165
0166
0167
0168 template<class RealType>
0169 template<class Generator>
0170 CELER_FUNCTION auto
0171 NormalDistribution<RealType>::operator()(Generator& rng) -> result_type
0172 {
0173 if (has_spare_)
0174 {
0175 has_spare_ = false;
0176 return std::fma(spare_, stddev_, mean_);
0177 }
0178
0179 constexpr auto twopi = static_cast<RealType>(2 * m_pi);
0180 real_type theta = twopi * generate_canonical<RealType>(rng);
0181 real_type r = std::sqrt(-2 * std::log(generate_canonical<RealType>(rng)));
0182 spare_ = r * std::cos(theta);
0183 has_spare_ = true;
0184 return std::fma(r * std::sin(theta), stddev_, mean_);
0185 }
0186
0187
0188 }