File indexing completed on 2025-02-22 10:31:28
0001
0002
0003
0004
0005
0006
0007
0008 #pragma once
0009
0010 #include "corecel/Assert.hh"
0011 #include "corecel/Macros.hh"
0012 #include "corecel/Types.hh"
0013 #include "corecel/math/ArrayOperators.hh"
0014 #include "corecel/math/ArrayUtils.hh"
0015 #include "celeritas/random/Selector.hh"
0016 #include "celeritas/random/distribution/ExponentialDistribution.hh"
0017 #include "celeritas/random/distribution/GenerateCanonical.hh"
0018 #include "celeritas/random/distribution/NormalDistribution.hh"
0019 #include "celeritas/random/distribution/RejectionSampler.hh"
0020 #include "celeritas/random/distribution/UniformRealDistribution.hh"
0021
0022 #include "GeneratorDistributionData.hh"
0023 #include "ScintillationData.hh"
0024 #include "TrackInitializer.hh"
0025
0026 #include "detail/OpticalUtils.hh"
0027
0028 namespace celeritas
0029 {
0030 namespace optical
0031 {
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051 class ScintillationGenerator
0052 {
0053 public:
0054
0055
0056 using Energy = units::MevEnergy;
0057
0058
0059 public:
0060
0061 inline CELER_FUNCTION
0062 ScintillationGenerator(NativeCRef<ScintillationData> const& shared,
0063 GeneratorDistributionData const& dist);
0064
0065
0066 template<class Generator>
0067 inline CELER_FUNCTION TrackInitializer operator()(Generator& rng);
0068
0069 private:
0070
0071
0072 using UniformRealDist = UniformRealDistribution<real_type>;
0073 using ExponentialDist = ExponentialDistribution<real_type>;
0074
0075
0076
0077 GeneratorDistributionData const& dist_;
0078 NativeCRef<ScintillationData> const& shared_;
0079
0080 UniformRealDist sample_cost_;
0081 UniformRealDist sample_phi_;
0082 NormalDistribution<real_type> sample_lambda_;
0083
0084 bool is_neutral_{};
0085 units::LightSpeed delta_speed_{};
0086 Real3 delta_pos_{};
0087 };
0088
0089
0090
0091
0092
0093
0094
0095 CELER_FUNCTION
0096 ScintillationGenerator::ScintillationGenerator(
0097 NativeCRef<ScintillationData> const& shared,
0098 GeneratorDistributionData const& dist)
0099 : dist_(dist)
0100 , shared_(shared)
0101 , sample_cost_(-1, 1)
0102 , sample_phi_(0, 2 * constants::pi)
0103 , is_neutral_{dist_.charge == zero_quantity()}
0104 {
0105 if (shared_.scintillation_by_particle())
0106 {
0107
0108 CELER_ASSERT_UNREACHABLE();
0109 }
0110
0111 CELER_EXPECT(dist_);
0112 CELER_EXPECT(shared_);
0113
0114 auto const& pre_step = dist_.points[StepPoint::pre];
0115 auto const& post_step = dist_.points[StepPoint::post];
0116 delta_pos_ = post_step.pos - pre_step.pos;
0117 delta_speed_ = post_step.speed - pre_step.speed;
0118 }
0119
0120
0121
0122
0123
0124 template<class Generator>
0125 CELER_FUNCTION TrackInitializer ScintillationGenerator::operator()(Generator& rng)
0126 {
0127
0128 ScintRecord const& component = [&] {
0129 auto const& mat = shared_.materials[dist_.material];
0130
0131 auto pdf = shared_.reals[mat.yield_pdf];
0132 auto select_idx = make_selector([&pdf](size_type i) { return pdf[i]; },
0133 mat.yield_pdf.size());
0134 size_type component_idx = select_idx(rng);
0135 CELER_ASSERT(component_idx < mat.components.size());
0136 return shared_.scint_records[mat.components[component_idx]];
0137 }();
0138
0139
0140
0141 sample_lambda_
0142 = NormalDistribution{component.lambda_mean, component.lambda_sigma};
0143 ExponentialDist sample_time(real_type{1} / component.fall_time);
0144
0145 TrackInitializer photon;
0146 photon.energy = detail::wavelength_to_energy(sample_lambda_(rng));
0147
0148
0149 real_type cost = sample_cost_(rng);
0150 real_type phi = sample_phi_(rng);
0151 photon.direction = from_spherical(cost, phi);
0152
0153
0154 photon.polarization = [&] {
0155 Real3 temp = from_spherical(
0156 (cost > 0 ? -1 : 1) * std::sqrt(1 - ipow<2>(cost)), phi);
0157 Real3 perp = {-std::sin(phi), std::cos(phi), 0};
0158 real_type sinphi, cosphi;
0159 sincospi(UniformRealDist{}(rng), &sinphi, &cosphi);
0160 for (auto j : range(3))
0161 {
0162 temp[j] = cosphi * temp[j] + sinphi * perp[j];
0163 }
0164 return make_unit_vector(temp);
0165 }();
0166
0167
0168 real_type u = is_neutral_ ? 1 : UniformRealDist{}(rng);
0169 photon.position = dist_.points[StepPoint::pre].pos;
0170 axpy(u, delta_pos_, &photon.position);
0171
0172
0173 photon.time
0174 = dist_.time
0175 + u * dist_.step_length
0176 / (native_value_from(dist_.points[StepPoint::pre].speed)
0177 + u * real_type(0.5) * native_value_from(delta_speed_));
0178
0179 if (component.rise_time == 0)
0180 {
0181
0182 photon.time += sample_time(rng);
0183 }
0184 else
0185 {
0186 real_type scint_time{};
0187 real_type target;
0188 do
0189 {
0190
0191
0192 scint_time = sample_time(rng);
0193 target = -std::expm1(-scint_time / component.rise_time);
0194 } while (RejectionSampler(target)(rng));
0195 photon.time += scint_time;
0196 }
0197 return photon;
0198 }
0199
0200
0201 }
0202 }