Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 09:08:59

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/em/interactor/AtomicRelaxation.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include "corecel/Macros.hh"
0010 #include "corecel/Types.hh"
0011 #include "corecel/cont/MiniStack.hh"
0012 #include "corecel/cont/Span.hh"
0013 #include "geocel/random/IsotropicDistribution.hh"
0014 #include "celeritas/Quantities.hh"
0015 #include "celeritas/Types.hh"
0016 #include "celeritas/em/data/AtomicRelaxationData.hh"
0017 #include "celeritas/phys/CutoffView.hh"
0018 #include "celeritas/phys/Secondary.hh"
0019 
0020 namespace celeritas
0021 {
0022 //---------------------------------------------------------------------------//
0023 /*!
0024  * Simulate particle emission from atomic deexcitation.
0025  *
0026  * The EADL radiative and non-radiative transition data is used to simulate the
0027  * emission of fluorescence photons and (optionally) Auger electrons given an
0028  * initial shell vacancy created by a primary process.
0029  */
0030 class AtomicRelaxation
0031 {
0032   public:
0033     //!@{
0034     //! \name Type aliases
0035     using Energy = units::MevEnergy;
0036     //!@}
0037 
0038     struct result_type
0039     {
0040         size_type count{};  //!< Number of secondaries created
0041         Energy energy{};  //!< Sum of the energies of the secondaries
0042     };
0043 
0044   public:
0045     // Construct with shared and state data
0046     inline CELER_FUNCTION AtomicRelaxation(AtomicRelaxParamsRef const& shared,
0047                                            CutoffView const& cutoffs,
0048                                            ElementId el_id,
0049                                            SubshellId shell_id,
0050                                            Span<Secondary> secondaries,
0051                                            Span<SubshellId> vacancies);
0052 
0053     // Simulate atomic relaxation with an initial vacancy in the given shell ID
0054     template<class Engine>
0055     inline CELER_FUNCTION result_type operator()(Engine& rng);
0056 
0057   private:
0058     // Shared EADL atomic relaxation data
0059     AtomicRelaxParamsRef const& shared_;
0060     // Photon production threshold [MeV]
0061     Energy gamma_cutoff_;
0062     // Electron production threshold [MeV]
0063     Energy electron_cutoff_;
0064     // Index in MaterialParams elements
0065     ElementId el_id_;
0066     // Shell ID of the initial vacancy
0067     SubshellId shell_id_;
0068     // Fluorescence photons and Auger electrons
0069     Span<Secondary> secondaries_;
0070     // Storage for stack of unprocessed subshell vacancies
0071     Span<SubshellId> vacancies_;
0072     // Angular distribution of secondaries
0073     IsotropicDistribution<real_type> sample_direction_;
0074 
0075   private:
0076     using TransitionId = OpaqueId<AtomicRelaxTransition>;
0077 
0078     template<class Engine>
0079     inline CELER_FUNCTION TransitionId
0080     sample_transition(AtomicRelaxSubshell const& shell, Engine& rng);
0081 };
0082 
0083 //---------------------------------------------------------------------------//
0084 // INLINE DEFINITIONS
0085 //---------------------------------------------------------------------------//
0086 /*!
0087  * Construct with shared and state data.
0088  *
0089  * The secondaries must have enough storage allocated for particles produced in
0090  * atomic relaxation and the vacancies must have enough storage allocated for
0091  * the stack of subshell IDs: this should be handled in code *before*
0092  * construction.
0093  *
0094  * The precondition of the element having relaxation data is satisfied by the
0095  * AtomicRelaxationHelper -- it is only "true" if a distribution can be
0096  * emitted.
0097  */
0098 CELER_FUNCTION
0099 AtomicRelaxation::AtomicRelaxation(AtomicRelaxParamsRef const& shared,
0100                                    CutoffView const& cutoffs,
0101                                    ElementId el_id,
0102                                    SubshellId shell_id,
0103                                    Span<Secondary> secondaries,
0104                                    Span<SubshellId> vacancies)
0105     : shared_(shared)
0106     , gamma_cutoff_(cutoffs.energy(shared_.ids.gamma))
0107     , electron_cutoff_(cutoffs.energy(shared_.ids.electron))
0108     , el_id_(el_id)
0109     , shell_id_(shell_id)
0110     , secondaries_(secondaries)
0111     , vacancies_(vacancies)
0112 {
0113     CELER_EXPECT(shared_ && el_id_ < shared_.elements.size()
0114                  && shared_.elements[el_id_]);
0115     CELER_EXPECT(shell_id);
0116 }
0117 
0118 //---------------------------------------------------------------------------//
0119 /*!
0120  * Simulate atomic relaxation with an initial vacancy in the given shell ID
0121  */
0122 template<class Engine>
0123 CELER_FUNCTION AtomicRelaxation::result_type
0124 AtomicRelaxation::operator()(Engine& rng)
0125 {
0126     AtomicRelaxElement const& el = shared_.elements[el_id_];
0127     auto const& shells = shared_.shells[el.shells];
0128     MiniStack<SubshellId> vacancies(vacancies_);
0129 
0130     // Push the vacancy created by the primary process onto a stack.
0131     vacancies.push(shell_id_);
0132 
0133     // Total number of secondaries
0134     size_type count = 0;
0135     real_type sum_energy = 0;
0136 
0137     // Generate the shower of photons and electrons produced by radiative and
0138     // non-radiative transitions
0139     while (!vacancies.empty())
0140     {
0141         // Pop the vacancy off the stack and check if it has transition data
0142         SubshellId vacancy_id = vacancies.pop();
0143         if (vacancy_id.get() >= shells.size())
0144             continue;
0145 
0146         // Sample a transition (TODO: refactor to use Selector but with
0147         // "remainder")
0148         AtomicRelaxSubshell const& shell = shells[vacancy_id.get()];
0149         TransitionId const trans_id = this->sample_transition(shell, rng);
0150 
0151         if (!trans_id)
0152             continue;
0153 
0154         // Push the new vacancies onto the stack and create the secondary
0155         auto const& transition
0156             = shared_.transitions[shell.transitions][trans_id.get()];
0157         vacancies.push(transition.initial_shell);
0158         if (transition.auger_shell)
0159         {
0160             vacancies.push(transition.auger_shell);
0161 
0162             if (transition.energy >= electron_cutoff_)
0163             {
0164                 // Sampled a non-radiative transition: create an Auger electron
0165                 CELER_ASSERT(count < secondaries_.size());
0166                 Secondary& secondary = secondaries_[count++];
0167                 secondary.direction = sample_direction_(rng);
0168                 secondary.energy = transition.energy;
0169                 secondary.particle_id = shared_.ids.electron;
0170 
0171                 // Accumulate the energy carried away by secondaries
0172                 sum_energy += value_as<Energy>(transition.energy);
0173             }
0174         }
0175         else if (transition.energy >= gamma_cutoff_)
0176         {
0177             // Sampled a radiative transition: create a fluorescence photon
0178             CELER_ASSERT(count < secondaries_.size());
0179             Secondary& secondary = secondaries_[count++];
0180             secondary.direction = sample_direction_(rng);
0181             secondary.energy = transition.energy;
0182             secondary.particle_id = shared_.ids.gamma;
0183 
0184             // Accumulate the energy carried away by secondaries
0185             sum_energy += value_as<Energy>(transition.energy);
0186         }
0187     }
0188 
0189     result_type result;
0190     result.count = count;
0191     result.energy = Energy{sum_energy};
0192     return result;
0193 }
0194 
0195 //---------------------------------------------------------------------------//
0196 /*!
0197  * Sample an atomic transition.
0198  *
0199  * TODO: refactor to use a Selector-like algorithm that allows a "remainder"
0200  * that indicates "not sampled".
0201  */
0202 template<class Engine>
0203 inline CELER_FUNCTION auto
0204 AtomicRelaxation::sample_transition(AtomicRelaxSubshell const& shell,
0205                                     Engine& rng) -> TransitionId
0206 {
0207     auto const& transitions = shared_.transitions[shell.transitions];
0208 
0209     real_type accum = -generate_canonical(rng);
0210     for (size_type i = 0; i < transitions.size(); ++i)
0211     {
0212         accum += transitions[i].probability;
0213         if (accum > 0)
0214             return TransitionId{i};
0215     }
0216 
0217     // No transition was sampled: skip to the next vacancy
0218     return {};
0219 }
0220 
0221 //---------------------------------------------------------------------------//
0222 }  // namespace celeritas