Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-19 08:49:41

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file celeritas/alongstep/detail/FluctELoss.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include "celeritas/em/data/FluctuationData.hh"
0010 #include "celeritas/em/distribution/EnergyLossHelper.hh"
0011 #include "celeritas/em/distribution/EnergyLossTraits.hh"
0012 #include "celeritas/global/CoreTrackView.hh"
0013 #include "celeritas/phys/PhysicsStepUtils.hh"
0014 
0015 namespace celeritas
0016 {
0017 namespace detail
0018 {
0019 //---------------------------------------------------------------------------//
0020 /*!
0021  * Apply energy loss (with fluctuations) to a track.
0022  */
0023 class FluctELoss
0024 {
0025   public:
0026     //!@{
0027     //! \name Type aliases
0028     using Energy = EnergyLossHelper::Energy;
0029     using ParamsRef = NativeCRef<FluctuationData>;
0030     //!@}
0031 
0032   public:
0033     // Construct with fluctuation data
0034     inline explicit CELER_FUNCTION FluctELoss(ParamsRef const& params);
0035 
0036     // Whether energy loss can be used for this track
0037     inline CELER_FUNCTION bool is_applicable(CoreTrackView const&) const;
0038 
0039     // Apply to the track
0040     inline CELER_FUNCTION Energy calc_eloss(CoreTrackView const& track,
0041                                             real_type step,
0042                                             bool apply_cut);
0043 
0044     //! Indicate that we can lose all energy before hitting the dE/dx range
0045     static CELER_CONSTEXPR_FUNCTION bool imprecise_range() { return true; }
0046 
0047   private:
0048     //// DATA ////
0049 
0050     //! Reference to fluctuation data
0051     ParamsRef const fluct_params_;
0052 
0053     //// HELPER FUNCTIONS ////
0054 
0055     template<EnergyLossFluctuationModel M>
0056     inline CELER_FUNCTION Energy
0057     sample_energy_loss(EnergyLossHelper const& helper, RngEngine& rng);
0058 };
0059 
0060 //---------------------------------------------------------------------------//
0061 // INLINE DEFINITIONS
0062 //---------------------------------------------------------------------------//
0063 /*!
0064  * Construct with reference to fluctuation data.
0065  */
0066 CELER_FUNCTION FluctELoss::FluctELoss(ParamsRef const& params)
0067     : fluct_params_{params}
0068 {
0069     CELER_EXPECT(fluct_params_);
0070 }
0071 
0072 //---------------------------------------------------------------------------//
0073 /*!
0074  * Whether energy loss is used for this track.
0075  */
0076 CELER_FUNCTION bool FluctELoss::is_applicable(CoreTrackView const& track) const
0077 {
0078     // The track can be marked as `errored` *within* the along-step kernel,
0079     // during propagation
0080     if (track.sim().status() == TrackStatus::errored)
0081         return false;
0082 
0083     // Energy loss grid ID is 'false'
0084     return static_cast<bool>(track.physics().energy_loss_grid());
0085 }
0086 
0087 //---------------------------------------------------------------------------//
0088 /*!
0089  * Apply energy loss to the given track.
0090  *
0091  * - Before and after slowing down we apply a tracking cut to cull low-energy
0092  *   charged particles.
0093  * - If energy loss fluctuations are enabled, we apply those based on the mean
0094  *   energy loss.
0095  * - If the sampled energy loss is greater than or equal to the particle's
0096  *   energy, we reduce it to the particle energy (if energy cuts are to be
0097  *   applied) or to the mean energy loss (if cuts are prohibited due to this
0098  *   being a non-physics-based step).
0099  */
0100 CELER_FUNCTION auto FluctELoss::calc_eloss(CoreTrackView const& track,
0101                                            real_type step,
0102                                            bool apply_cut) -> Energy
0103 {
0104     CELER_EXPECT(step > 0);
0105 
0106     auto particle = track.particle();
0107     auto phys = track.physics();
0108 
0109     if (apply_cut && particle.energy() < phys.particle_scalars().lowest_energy)
0110     {
0111         // Deposit all energy immediately when we start below the tracking cut
0112         return particle.energy();
0113     }
0114 
0115     // Calculate mean energy loss
0116     auto eloss = calc_mean_energy_loss(particle, phys, step);
0117 
0118     if (eloss < particle.energy() && eloss > zero_quantity())
0119     {
0120         // Apply energy loss fluctuations
0121         auto cutoffs = track.cutoff();
0122         auto material = track.material();
0123 
0124         EnergyLossHelper loss_helper(
0125             fluct_params_, cutoffs, material, particle, eloss, step);
0126 
0127         auto rng = track.rng();
0128         switch (loss_helper.model())
0129         {
0130 #define ASU_SAMPLE_ELOSS(MODEL)                                              \
0131     case EnergyLossFluctuationModel::MODEL:                                  \
0132         eloss = this->sample_energy_loss<EnergyLossFluctuationModel::MODEL>( \
0133             loss_helper, rng);                                               \
0134         break
0135             ASU_SAMPLE_ELOSS(none);
0136             ASU_SAMPLE_ELOSS(gamma);
0137             ASU_SAMPLE_ELOSS(gaussian);
0138             ASU_SAMPLE_ELOSS(urban);
0139 #undef ASU_SAMPLE_ELOSS
0140         }
0141 
0142         if (eloss >= particle.energy())
0143         {
0144             // Sampled energy loss can be greater than actual remaining energy
0145             // because the range calculation is based on the *mean* energy
0146             // loss.
0147             if (apply_cut)
0148             {
0149                 // Clamp to actual particle energy so that it stops
0150                 eloss = particle.energy();
0151             }
0152             else
0153             {
0154                 // Don't go to zero energy at geometry boundaries: just use the
0155                 // mean loss which should be positive because this isn't a
0156                 // range-limited step.
0157                 eloss = loss_helper.mean_loss();
0158                 CELER_ASSERT(eloss < particle.energy());
0159             }
0160         }
0161     }
0162 
0163     if (apply_cut
0164         && (particle.energy() - eloss <= phys.particle_scalars().lowest_energy))
0165     {
0166         // Deposit all energy when we end below the tracking cut
0167         return particle.energy();
0168     }
0169 
0170     CELER_ASSERT(eloss <= particle.energy());
0171     CELER_ENSURE(eloss != particle.energy() || apply_cut
0172                  || track.sim().post_step_action()
0173                         == phys.scalars().range_action());
0174     return eloss;
0175 }
0176 
0177 //---------------------------------------------------------------------------//
0178 template<EnergyLossFluctuationModel M>
0179 CELER_FUNCTION auto
0180 FluctELoss::sample_energy_loss(EnergyLossHelper const& helper,
0181                                RngEngine& rng) -> Energy
0182 {
0183     CELER_EXPECT(helper.model() == M);
0184 
0185     using Distribution = typename EnergyLossTraits<M>::type;
0186 
0187     Distribution sample_eloss{helper};
0188     return sample_eloss(rng);
0189 }
0190 
0191 //---------------------------------------------------------------------------//
0192 }  // namespace detail
0193 }  // namespace celeritas