Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:53:33

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDmu-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file celeritas/em/distribution/UrbanLargeAngleDistribution.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <cmath>
0010 
0011 #include "corecel/Assert.hh"
0012 #include "corecel/Macros.hh"
0013 #include "corecel/Types.hh"
0014 #include "corecel/math/Algorithms.hh"
0015 #include "corecel/random/distribution/BernoulliDistribution.hh"
0016 #include "corecel/random/distribution/PowerDistribution.hh"
0017 #include "corecel/random/distribution/UniformRealDistribution.hh"
0018 #include "corecel/random/engine/CachedRngEngine.hh"
0019 
0020 namespace celeritas
0021 {
0022 //---------------------------------------------------------------------------//
0023 /*!
0024  * Sample the large-angle MSC scattering cosine.
0025  *
0026  * \citet{urban-msc-2006,
0027  * https://cds.cern.ch/record/1004190/} proposes a convex combination of three
0028  * probability distribution functions:
0029  * \f[
0030  * \begin{aligned}
0031  *  g_0(\mu) &\sim \exp(-a(1 - \mu)), \\
0032  *  g_1(\mu) &\sim (b - \mu)^{-d}, \\
0033  *  g_2(\mu) &\sim 1
0034  * \end{aligned}
0035  * \f]
0036  * which have normalizing constants and sum to
0037  * \f[
0038  * g(\mu) = p_1 p_2 g_0(\mu) + p_1(1-p_2) g_1(\mu) + (1-p_1) g_2(\mu).
0039  * \f]
0040  *
0041  * In this distribution for large angles, \f$ p_2 = 1 \f$ so only the
0042  * exponential and constant terms are sampled.
0043  *
0044  *
0045  * The Goudsmit-Saunderson moments for the expected angular deflection
0046  * \f$ \theta \f$ over a physical path length \f$ s \f$ are: \f[
0047     \langle \cos \theta \rangle
0048      \equiv \langle \mu \rangle
0049      = \ee^{-s/\lambda_1} \ ,
0050  * \f] and \f[
0051     \langle \cos^2 \theta \rangle
0052       \equiv \langle \mu^2 \rangle
0053       = \frac{1}{3}\left(1 + 2 \ee^{-s / \lambda_2}\right) \ ,
0054  * \f]
0055  * where the transport mean free paths \f$ \lambda_l \f$ are related to the
0056  * \f$ l \f$th angular moment of the elastic cross section scattering  (see
0057  * Eqs. 6-8, 15-16 from \citet{fernandez-msc-1993,
0058  * https://doi.org/10.1016/0168-583X(93)95827-R}
0059  * ).
0060  *
0061  * Given the number of mean free paths \f[
0062     \tau \equiv \frac{s}{\lambda_1} \ ,
0063  * \f]
0064  * and from \citet{kawrakow-condensedhistory-1998,
0065  * https://doi.org/10.1016/S0168-583X(98)00274-2} that for kinetic energies
0066  * between a few keV and infinity, \f[
0067    2 < \frac{\lambda_2}{\lambda_1} < \infty \ ,
0068  * \f]
0069  * this class calculates the mean scattering angle and approximates the second
0070  * moment of the scattering cosine using
0071  * \f$ \lambda_2 \approx 2.5 \lambda_1 \f$.
0072  *
0073  * Using these moments, Urban calculates: \f[
0074    a = \frac{2\langle \mu \rangle + 9\langle \mu^2 \rangle - 3}
0075             {2\langle \mu \rangle - 3\langle \mu^2 \rangle + 1}
0076  * \f] and
0077  * \f[
0078    p_1 = \frac{(a + 2)\langle \mu \rangle}{a} \,.
0079  * \f]
0080  */
0081 class UrbanLargeAngleDistribution
0082 {
0083   public:
0084     // Construct with mean free path tau
0085     explicit inline CELER_FUNCTION UrbanLargeAngleDistribution(real_type tau);
0086 
0087     // Sample cos(theta)
0088     template<class Engine>
0089     inline CELER_FUNCTION real_type operator()(Engine& rng);
0090 
0091   private:
0092     BernoulliDistribution select_pow_{0};
0093     PowerDistribution<> sample_pow_{0};
0094     UniformRealDistribution<> sample_uniform_{};
0095 };
0096 
0097 //---------------------------------------------------------------------------//
0098 // INLINE DEFINITIONS
0099 //---------------------------------------------------------------------------//
0100 /*!
0101  * Construct with mean values.
0102  */
0103 CELER_FUNCTION
0104 UrbanLargeAngleDistribution::UrbanLargeAngleDistribution(real_type tau)
0105 {
0106     CELER_EXPECT(tau > 0);
0107     // Eq. 8.2 and \f$ \cos^2\theta \f$ term in Eq. 8.3 in PRM
0108     real_type mumean = std::exp(-tau);
0109     // NOTE: tau_big = 8 -> ~0.0003 < mumean < 1
0110 
0111     real_type musqmean = (1 + 2 * std::exp(real_type(-2.5) * tau)) / 3;
0112     real_type a = (2 * mumean + 9 * musqmean - 3)
0113                   / (2 * mumean - 3 * musqmean + 1);
0114 
0115     select_pow_ = BernoulliDistribution{mumean * (1 + 2 / a)};
0116     sample_pow_ = PowerDistribution<>{a};
0117 }
0118 
0119 //---------------------------------------------------------------------------//
0120 /*!
0121  * Sample from two parameters of the model function.
0122  *
0123  * \todo The cached RNG value is not necessary and is only for reproducing
0124  * previous results. It should be deleted the next time a rebaselining is
0125  * performed.
0126  */
0127 template<class Engine>
0128 CELER_FUNCTION real_type UrbanLargeAngleDistribution::operator()(Engine& rng)
0129 {
0130     // Save the RNG result to preserve RNG stream from older Celeritas
0131     auto temp_rng = cache_rng_values<real_type, 1>(rng);
0132     // Sample u = (cos \theta + 1)/ 2
0133     real_type half_angle = select_pow_(rng) ? sample_pow_(temp_rng)
0134                                             : sample_uniform_(temp_rng);
0135     CELER_ASSERT(half_angle >= 0 && half_angle <= 1);
0136 
0137     // Transform back to [-1, 1]
0138     return 2 * half_angle - 1;
0139 }
0140 
0141 //---------------------------------------------------------------------------//
0142 }  // namespace celeritas