Back to home page

EIC code displayed by LXR

 
 

    


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

0001 //----------------------------------*-C++-*----------------------------------//
0002 // Copyright 2021-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/distribution/SBEnergyDistribution.hh
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include "corecel/grid/TwodSubgridCalculator.hh"
0011 #include "corecel/math/Algorithms.hh"
0012 #include "celeritas/Quantities.hh"
0013 #include "celeritas/em/data/SeltzerBergerData.hh"
0014 #include "celeritas/random/distribution/RejectionSampler.hh"
0015 
0016 #include "SBEnergyDistHelper.hh"
0017 
0018 namespace celeritas
0019 {
0020 //---------------------------------------------------------------------------//
0021 /*!
0022  * Default scaling for SB cross sections.
0023  */
0024 struct SBElectronXsCorrector
0025 {
0026     using Xs = Quantity<SBElementTableData::XsUnits>;
0027 
0028     //! No cross section scaling for any exiting energy
0029     CELER_FUNCTION real_type operator()(units::MevEnergy) const { return 1; }
0030 };
0031 
0032 //---------------------------------------------------------------------------//
0033 /*!
0034  * Sample exiting photon energy from Bremsstrahlung.
0035  *
0036  * The SB energy distribution uses tabulated Seltzer-Berger differential cross
0037  * section data from G4EMLOW, which stores scaled cross sections as a function
0038  * of incident particle energy and exiting gamma energy (see SeltzerBergerModel
0039  * for details). The sampling procedure is roughly laid out in section
0040  * [PHYS341] of the GEANT3 physics reference manual, although like Geant4 we
0041  * use raw tabulated SB data rather than a parameter fit. Also like Geant4 we
0042  * include the extra density correction factor.
0043  *
0044  * Once an element and incident energy have been selected, the exiting energy
0045  * distribution is the differential cross section, which is stored as a scaled
0046  * tabulated value. The reconstructed cross section gives the pdf
0047  * \f[
0048  *   p(\kappa) \propto \difd{\sigma}{k} s
0049                \propto \frac{1}{\kappa} \chi_Z(E, \kappa)
0050  * \f]
0051  * where \f$ \kappa = k / E \f$ is the ratio of the emitted photon energy to
0052  * the incident charged particle energy, and the domain of \em p is
0053  * nominally restricted by the allowable energy range \f$ \kappa_c < \kappa < 1
0054  * \f$, where \f$ \kappa_c \f$ is from the cutoff energy \f$ E_c \f$ for
0055  * secondary gamma production. This distribution is sampled by decomposing it
0056  * into an analytic sampling of \f$ 1/\kappa \f$ and a rejection sample on
0057  * the scaled cross section \f$ \chi \f$.
0058  * The analytic sample over the domain is from \f[
0059    p_1(\kappa) \frac{1}{\ln 1/\kappa_c} \frac{1}{\kappa}
0060  * \f]
0061  * by sampling
0062  * \f[
0063    \kappa = \exp( \xi \ln \kappa_c ) \,,
0064    \f]
0065  * and the rejection sample is the ratio of the scaled differential cross
0066  * section at the exiting energy to the maximum across all exiting energies.
0067  * \f[
0068    p_2(\kappa) = \frac{\chi_Z(E, \kappa)}{\max_\kappa \chi_Z(E, \kappa)}
0069    \f]
0070  * Since the tabulated data is bilinearly interpolated in incident and exiting
0071  * energy, we can calculate a bounding maximum by precalculating (at
0072  * setup time) the index of the maximum value of \f$ \chi \f$ and
0073  * linearly interpolating those maximum values based on the incident energy.
0074  *
0075  * The above algorithm is idealized; in practice, the minimum and maximum
0076  * values are adjusted for a constant factor \f$d_\rho\f$, which depends on the
0077  * incident particle mass + energy and the material's electron density:
0078  * \f[
0079     \kappa_\mathrm{min} = \ln (E_c^2 + d_\rho E^2)
0080  * \f]
0081  * and
0082  * \f[
0083     \kappa_\mathrm{max} = \ln (E^2 + d_\rho E^2) \, .
0084  * \f]
0085  * With this correction, the sample of the exiting gamma energy
0086  * \f$ k = \kappa / E \f$ is transformed from the simple exponential above to:
0087  * \f[
0088  *  k = \sqrt{ \exp(\kappa_\mathrm{min} + \xi [\kappa_\mathrm{max} -
0089  \kappa_\mathrm{min}]) - d_\rho E^2}
0090  * \f]
0091  *
0092  * Most of the mechanics of the sampling are in the template-free
0093  * \c SBEnergyDistHelper, which is passed as a construction argument to this
0094  * sampler. The separate class exists here to minimize duplication of templated
0095  * code, which is required to provide for an on-the-fly correction of the cross
0096  * section sampling.
0097  */
0098 template<class XSCorrector>
0099 class SBEnergyDistribution
0100 {
0101   public:
0102     //!@{
0103     //! \name Type aliases
0104     using SBData = NativeCRef<SeltzerBergerData>;
0105     using Energy = units::MevEnergy;
0106     using EnergySq = Quantity<UnitProduct<units::Mev, units::Mev>>;
0107     //!@}
0108 
0109   public:
0110     // Construct from data
0111     inline CELER_FUNCTION SBEnergyDistribution(SBEnergyDistHelper const& helper,
0112                                                XSCorrector scale_xs);
0113 
0114     template<class Engine>
0115     inline CELER_FUNCTION Energy operator()(Engine& rng);
0116 
0117   private:
0118     //// IMPLEMENTATION DATA ////
0119     SBEnergyDistHelper const& helper_;
0120     XSCorrector scale_xs_;
0121 };
0122 
0123 //---------------------------------------------------------------------------//
0124 // INLINE DEFINITIONS
0125 //---------------------------------------------------------------------------//
0126 /*!
0127  * Construct from incident particle and energy.
0128  *
0129  * The incident energy *must* be within the bounds of the SB table data, so the
0130  * Model's applicability must be consistent with the table data.
0131  */
0132 template<class X>
0133 CELER_FUNCTION
0134 SBEnergyDistribution<X>::SBEnergyDistribution(SBEnergyDistHelper const& helper,
0135                                               X scale_xs)
0136     : helper_(helper), scale_xs_(::celeritas::move(scale_xs))
0137 {
0138 }
0139 
0140 //---------------------------------------------------------------------------//
0141 /*!
0142  * Sample the exiting energy by doing a table lookup and rejection.
0143  */
0144 template<class X>
0145 template<class Engine>
0146 CELER_FUNCTION auto SBEnergyDistribution<X>::operator()(Engine& rng) -> Energy
0147 {
0148     // Sampled energy
0149     Energy exit_energy;
0150     // Calculated cross section used inside rejection sampling
0151     real_type xs{};
0152     do
0153     {
0154         // Sample scaled energy and subtract correction factor
0155         exit_energy = helper_.sample_exit_energy(rng);
0156 
0157         // Interpolate the differential cross setion at the sampled exit energy
0158         xs = helper_.calc_xs(exit_energy).value() * scale_xs_(exit_energy);
0159     } while (RejectionSampler<>(xs, helper_.max_xs().value())(rng));
0160     return exit_energy;
0161 }
0162 
0163 //---------------------------------------------------------------------------//
0164 }  // namespace celeritas