File indexing completed on 2025-09-17 08:53:43
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include <cmath>
0010
0011 #include "corecel/Assert.hh"
0012 #include "corecel/Macros.hh"
0013 #include "corecel/Types.hh"
0014 #include "corecel/data/Collection.hh"
0015 #include "corecel/math/ArrayOperators.hh"
0016 #include "corecel/math/ArrayUtils.hh"
0017 #include "corecel/random/distribution/BernoulliDistribution.hh"
0018 #include "corecel/random/distribution/RejectionSampler.hh"
0019 #include "corecel/random/distribution/UniformRealDistribution.hh"
0020 #include "celeritas/grid/NonuniformGridCalculator.hh"
0021
0022 #include "CherenkovDndxCalculator.hh"
0023 #include "GeneratorDistributionData.hh"
0024 #include "MaterialView.hh"
0025 #include "TrackInitializer.hh"
0026
0027 namespace celeritas
0028 {
0029 namespace optical
0030 {
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049 class CherenkovGenerator
0050 {
0051 public:
0052
0053 inline CELER_FUNCTION
0054 CherenkovGenerator(MaterialView const& material,
0055 NativeCRef<CherenkovData> const& shared,
0056 GeneratorDistributionData const& dist);
0057
0058
0059 template<class Generator>
0060 inline CELER_FUNCTION TrackInitializer operator()(Generator& rng);
0061
0062 private:
0063
0064
0065 using UniformRealDist = UniformRealDistribution<real_type>;
0066
0067
0068
0069 GeneratorDistributionData const& dist_;
0070 NonuniformGridCalculator calc_refractive_index_;
0071 UniformRealDist sample_phi_;
0072 UniformRealDist sample_num_photons_;
0073 UniformRealDist sample_energy_;
0074 Real3 dir_;
0075 Real3 delta_pos_;
0076 units::LightSpeed delta_speed_;
0077 real_type delta_num_photons_;
0078 real_type dndx_pre_;
0079 real_type sin_max_sq_;
0080 real_type inv_beta_;
0081 };
0082
0083
0084
0085
0086
0087
0088
0089 CELER_FUNCTION
0090 CherenkovGenerator::CherenkovGenerator(MaterialView const& material,
0091 NativeCRef<CherenkovData> const& shared,
0092 GeneratorDistributionData const& dist)
0093 : dist_(dist)
0094 , calc_refractive_index_(material.make_refractive_index_calculator())
0095 , sample_phi_(0, real_type(2 * constants::pi))
0096 {
0097 CELER_EXPECT(shared);
0098 CELER_EXPECT(dist_);
0099 CELER_EXPECT(material.material_id() == dist_.material);
0100
0101 using LS = units::LightSpeed;
0102
0103
0104
0105 auto const& pre_step = dist_.points[StepPoint::pre];
0106 auto const& post_step = dist_.points[StepPoint::post];
0107 CherenkovDndxCalculator calc_dndx(material, shared, dist_.charge);
0108 dndx_pre_ = calc_dndx(pre_step.speed);
0109 real_type dndx_post = calc_dndx(post_step.speed);
0110
0111
0112 sample_num_photons_ = UniformRealDist(0, max(dndx_pre_, dndx_post));
0113
0114
0115 auto const& energy_grid = calc_refractive_index_.grid();
0116 sample_energy_ = UniformRealDist(energy_grid.front(), energy_grid.back());
0117
0118
0119 inv_beta_
0120 = 2 / (value_as<LS>(pre_step.speed) + value_as<LS>(post_step.speed));
0121 CELER_ASSERT(inv_beta_ > 1);
0122 real_type cos_max = inv_beta_ / calc_refractive_index_(energy_grid.back());
0123 sin_max_sq_ = 1 - ipow<2>(cos_max);
0124
0125
0126 delta_pos_ = post_step.pos - pre_step.pos;
0127 delta_num_photons_ = dndx_post - dndx_pre_;
0128 delta_speed_ = post_step.speed - pre_step.speed;
0129
0130
0131 dir_ = make_unit_vector(delta_pos_);
0132 }
0133
0134
0135
0136
0137
0138 template<class Generator>
0139 CELER_FUNCTION TrackInitializer CherenkovGenerator::operator()(Generator& rng)
0140 {
0141
0142 real_type energy;
0143 real_type cos_theta;
0144 real_type sin_theta_sq;
0145 do
0146 {
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157 do
0158 {
0159 energy = sample_energy_(rng);
0160 cos_theta = inv_beta_ / calc_refractive_index_(energy);
0161 } while (cos_theta > 1);
0162 sin_theta_sq = 1 - ipow<2>(cos_theta);
0163 } while (RejectionSampler{sin_theta_sq, sin_max_sq_}(rng));
0164
0165
0166 real_type phi = sample_phi_(rng);
0167 TrackInitializer photon;
0168 photon.direction = rotate(from_spherical(cos_theta, phi), dir_);
0169 photon.energy = units::MevEnergy(energy);
0170
0171
0172 photon.polarization
0173 = rotate(from_spherical(-std::sqrt(sin_theta_sq), phi), dir_);
0174
0175
0176 UniformRealDistribution sample_step_fraction;
0177 real_type u;
0178 do
0179 {
0180 u = sample_step_fraction(rng);
0181 } while (sample_num_photons_(rng) > dndx_pre_ + u * delta_num_photons_);
0182
0183 real_type delta_time
0184 = u * dist_.step_length
0185 / (native_value_from(dist_.points[StepPoint::pre].speed)
0186 + u * real_type(0.5) * native_value_from(delta_speed_));
0187 photon.time = dist_.time + delta_time;
0188 photon.position = dist_.points[StepPoint::pre].pos;
0189 axpy(u, delta_pos_, &photon.position);
0190 return photon;
0191 }
0192
0193
0194 }
0195 }