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