File indexing completed on 2026-01-07 10:01:42
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/math/ArrayOperators.hh"
0015 #include "corecel/math/ArrayUtils.hh"
0016 #include "corecel/random/distribution/RejectionSampler.hh"
0017 #include "corecel/random/distribution/UniformRealDistribution.hh"
0018 #include "celeritas/grid/NonuniformGridCalculator.hh"
0019
0020 #include "CherenkovDndxCalculator.hh"
0021 #include "GeneratorData.hh"
0022 #include "../MaterialView.hh"
0023 #include "../TrackInitializer.hh"
0024
0025 namespace celeritas
0026 {
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045 class CherenkovGenerator
0046 {
0047 public:
0048
0049 inline CELER_FUNCTION
0050 CherenkovGenerator(optical::MaterialView const& material,
0051 NativeCRef<CherenkovData> const& shared,
0052 GeneratorDistributionData const& dist);
0053
0054
0055 template<class Generator>
0056 inline CELER_FUNCTION optical::TrackInitializer operator()(Generator& rng);
0057
0058 private:
0059
0060
0061 using UniformRealDist = UniformRealDistribution<real_type>;
0062
0063
0064
0065 GeneratorDistributionData const& dist_;
0066 NonuniformGridCalculator calc_refractive_index_;
0067 UniformRealDist sample_phi_;
0068 UniformRealDist sample_num_photons_;
0069 UniformRealDist sample_energy_;
0070 Real3 dir_;
0071 Real3 delta_pos_;
0072 units::LightSpeed delta_speed_;
0073 real_type delta_num_photons_;
0074 real_type dndx_pre_;
0075 real_type sin_max_sq_;
0076 real_type inv_beta_;
0077 };
0078
0079
0080
0081
0082
0083
0084
0085 CELER_FUNCTION
0086 CherenkovGenerator::CherenkovGenerator(optical::MaterialView const& material,
0087 NativeCRef<CherenkovData> const& shared,
0088 GeneratorDistributionData const& dist)
0089 : dist_(dist)
0090 , calc_refractive_index_(material.make_refractive_index_calculator())
0091 , sample_phi_(0, real_type(2 * constants::pi))
0092 {
0093 CELER_EXPECT(shared);
0094 CELER_EXPECT(dist_);
0095 CELER_EXPECT(material.material_id() == dist_.material);
0096
0097 using LS = units::LightSpeed;
0098
0099
0100
0101 auto const& pre_step = dist_.points[StepPoint::pre];
0102 auto const& post_step = dist_.points[StepPoint::post];
0103 CherenkovDndxCalculator calc_dndx(material, shared, dist_.charge);
0104 dndx_pre_ = calc_dndx(pre_step.speed);
0105 real_type dndx_post = calc_dndx(post_step.speed);
0106
0107
0108 sample_num_photons_ = UniformRealDist(0, max(dndx_pre_, dndx_post));
0109
0110
0111 auto const& energy_grid = calc_refractive_index_.grid();
0112 sample_energy_ = UniformRealDist(energy_grid.front(), energy_grid.back());
0113
0114
0115
0116 inv_beta_
0117 = 2 / (value_as<LS>(pre_step.speed) + value_as<LS>(post_step.speed));
0118 CELER_ASSERT(inv_beta_ >= 1);
0119 real_type cos_max = inv_beta_ / calc_refractive_index_(energy_grid.back());
0120 sin_max_sq_ = 1 - ipow<2>(cos_max);
0121
0122
0123 delta_pos_ = post_step.pos - pre_step.pos;
0124 delta_num_photons_ = dndx_post - dndx_pre_;
0125 delta_speed_ = post_step.speed - pre_step.speed;
0126
0127
0128 dir_ = make_unit_vector(delta_pos_);
0129 }
0130
0131
0132
0133
0134
0135 template<class Generator>
0136 CELER_FUNCTION optical::TrackInitializer
0137 CherenkovGenerator::operator()(Generator& rng)
0138 {
0139
0140 real_type energy;
0141 real_type cos_theta;
0142 real_type sin_theta_sq;
0143 do
0144 {
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155 do
0156 {
0157 energy = sample_energy_(rng);
0158 cos_theta = inv_beta_ / calc_refractive_index_(energy);
0159 } while (cos_theta > 1);
0160 sin_theta_sq = 1 - ipow<2>(cos_theta);
0161 } while (RejectionSampler{sin_theta_sq, sin_max_sq_}(rng));
0162
0163
0164 real_type phi = sample_phi_(rng);
0165 optical::TrackInitializer photon;
0166 photon.direction = rotate(from_spherical(cos_theta, phi), dir_);
0167 photon.energy = units::MevEnergy(energy);
0168
0169
0170 photon.polarization = make_orthogonal(
0171 rotate(from_spherical(-std::sqrt(sin_theta_sq), phi), dir_),
0172 photon.direction);
0173 CELER_ASSERT(soft_zero(dot_product(photon.polarization, photon.direction)));
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 }