File indexing completed on 2025-09-16 08:52:25
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include "corecel/Assert.hh"
0010 #include "corecel/Macros.hh"
0011 #include "corecel/random/distribution/NormalDistribution.hh"
0012 #include "corecel/random/distribution/PoissonDistribution.hh"
0013 #include "celeritas/Quantities.hh"
0014 #include "celeritas/phys/ParticleTrackView.hh"
0015 #include "celeritas/track/SimTrackView.hh"
0016
0017 #include "GeneratorDistributionData.hh"
0018 #include "OffloadData.hh"
0019 #include "ScintillationData.hh"
0020
0021 namespace celeritas
0022 {
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038 class ScintillationOffload
0039 {
0040 public:
0041
0042 inline CELER_FUNCTION
0043 ScintillationOffload(ParticleTrackView const& particle,
0044 SimTrackView const& sim,
0045 Real3 const& pos,
0046 units::MevEnergy energy_deposition,
0047 NativeCRef<optical::ScintillationData> const& shared,
0048 OffloadPreStepData const& step_data);
0049
0050
0051 template<class Generator>
0052 inline CELER_FUNCTION optical::GeneratorDistributionData
0053 operator()(Generator& rng);
0054
0055 private:
0056 units::ElementaryCharge charge_;
0057 real_type step_length_;
0058 OffloadPreStepData const& pre_step_;
0059 optical::GeneratorStepData post_step_;
0060 NativeCRef<optical::ScintillationData> const& shared_;
0061 real_type mean_num_photons_{0};
0062
0063 static CELER_CONSTEXPR_FUNCTION real_type poisson_threshold()
0064 {
0065 return 10;
0066 }
0067 };
0068
0069
0070
0071
0072
0073
0074
0075 CELER_FUNCTION ScintillationOffload::ScintillationOffload(
0076 ParticleTrackView const& particle,
0077 SimTrackView const& sim,
0078 Real3 const& pos,
0079 units::MevEnergy energy_deposition,
0080 NativeCRef<optical::ScintillationData> const& shared,
0081 OffloadPreStepData const& step_data)
0082 : charge_(particle.charge())
0083 , step_length_(sim.step_length())
0084 , pre_step_(step_data)
0085 , post_step_({particle.speed(), pos})
0086 , shared_(shared)
0087 {
0088 CELER_EXPECT(step_length_ > 0);
0089 CELER_EXPECT(shared_);
0090 CELER_EXPECT(pre_step_);
0091
0092 if (shared_.scintillation_by_particle())
0093 {
0094
0095
0096 CELER_ASSERT_UNREACHABLE();
0097 }
0098 else
0099 {
0100
0101 CELER_ASSERT(pre_step_.material < shared_.materials.size());
0102 auto const& material = shared_.materials[pre_step_.material];
0103
0104
0105 if (material)
0106 {
0107 mean_num_photons_ = material.yield_per_energy
0108 * energy_deposition.value();
0109 }
0110 }
0111 }
0112
0113
0114
0115
0116
0117
0118 template<class Generator>
0119 CELER_FUNCTION optical::GeneratorDistributionData
0120 ScintillationOffload::operator()(Generator& rng)
0121 {
0122
0123 optical::GeneratorDistributionData result;
0124 if (mean_num_photons_ > poisson_threshold())
0125 {
0126 real_type sigma = shared_.resolution_scale[pre_step_.material]
0127 * std::sqrt(mean_num_photons_);
0128 result.num_photons = static_cast<size_type>(clamp_to_nonneg(
0129 NormalDistribution<real_type>(mean_num_photons_, sigma)(rng)
0130 + real_type{0.5}));
0131 }
0132 else if (mean_num_photons_ > 0)
0133 {
0134 result.num_photons = static_cast<size_type>(
0135 PoissonDistribution<real_type>(mean_num_photons_)(rng));
0136 }
0137
0138 if (result.num_photons > 0)
0139 {
0140
0141 result.time = pre_step_.time;
0142 result.step_length = step_length_;
0143 result.charge = charge_;
0144 result.material = pre_step_.material;
0145 result.points[StepPoint::pre].speed = pre_step_.speed;
0146 result.points[StepPoint::pre].pos = pre_step_.pos;
0147 result.points[StepPoint::post] = post_step_;
0148 }
0149 return result;
0150 }
0151
0152
0153 }