Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-22 10:31:17

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