Back to home page

EIC code displayed by LXR

 
 

    


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

0001 //----------------------------------*-C++-*----------------------------------//
0002 // Copyright 2023-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/msc/detail/MscStepFromGeo.hh
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include <cmath>
0011 
0012 #include "corecel/math/Algorithms.hh"
0013 #include "corecel/math/NumericLimits.hh"
0014 #include "corecel/math/SoftEqual.hh"
0015 #include "celeritas/Types.hh"
0016 #include "celeritas/em/data/UrbanMscData.hh"
0017 #include "celeritas/phys/Interaction.hh"
0018 
0019 namespace celeritas
0020 {
0021 namespace detail
0022 {
0023 //---------------------------------------------------------------------------//
0024 /*!
0025  * Convert the "geometrical" path traveled to the true path with MSC.
0026  *
0027  * The "true" step length is the physical path length taken along the
0028  * geometrical path length (which is straight without magnetic fields or curved
0029  * with them), accounting for the extra distance taken between
0030  * along-step elastic collisions.
0031  *
0032  * The transformation can be written as
0033  * \f[
0034  *     t(z) = \langle t \rangle = -\lambda_{1} \log(1 - \frac{z}{\lambda_{1}})
0035  * \f]
0036  * or \f$ t(z) = \frac{1}{\alpha} [ 1 - (1-\alpha w z)^{1/w}] \f$ if the
0037  * geom path is small, where \f$ w = 1 + \frac{1}{\alpha \lambda_{10}}\f$.
0038  *
0039  * \param true_path the proposed step before transportation.
0040  * \param gstep the proposed step after transportation.
0041  * \param alpha variable from UrbanMscStepLimit.
0042  */
0043 class MscStepFromGeo
0044 {
0045   public:
0046     //!@{
0047     //! \name Type aliases
0048     using MscParameters = UrbanMscParameters;
0049     //!@}
0050 
0051   public:
0052     // Construct with path length data
0053     inline CELER_FUNCTION MscStepFromGeo(MscParameters const& params_,
0054                                          MscStep const& step,
0055                                          real_type range,
0056                                          real_type lambda);
0057 
0058     // Calculate the true path
0059     inline CELER_FUNCTION real_type operator()(real_type gstep) const;
0060 
0061   private:
0062     // Urban MSC parameters
0063     MscParameters const& params_;
0064     //! Physical path length limited and transformed by MSC
0065     real_type true_step_;
0066     //! Scaled slope of the MFP
0067     real_type alpha_;
0068     //! Range for checking edge cases
0069     real_type range_;
0070     //! MFP at the start of step
0071     real_type lambda_;
0072 };
0073 
0074 //---------------------------------------------------------------------------//
0075 // INLINE DEFINITIONS
0076 //---------------------------------------------------------------------------//
0077 /*!
0078  * Construct from MSC data.
0079  */
0080 CELER_FUNCTION MscStepFromGeo::MscStepFromGeo(MscParameters const& params,
0081                                               MscStep const& step,
0082                                               real_type range,
0083                                               real_type lambda)
0084     : params_(params)
0085     , true_step_(step.true_path)
0086     , alpha_(step.alpha)
0087     , range_(range)
0088     , lambda_(lambda)
0089 {
0090     CELER_EXPECT(lambda_ > 0);
0091     CELER_EXPECT(true_step_ <= range_);
0092     // TODO: expect that the "skip sampling" conditions are false.
0093 }
0094 
0095 //---------------------------------------------------------------------------//
0096 /*!
0097  * Calculate and return the true path from the input "geometrical" step.
0098  *
0099  * This should only be applied if the step was geometry-limited. Otherwise, the
0100  * original interaction length is correct.
0101  */
0102 CELER_FUNCTION real_type MscStepFromGeo::operator()(real_type gstep) const
0103 {
0104     CELER_EXPECT(gstep >= 0 && gstep <= true_step_);
0105 
0106     if (gstep < params_.min_step())
0107     {
0108         // Geometrical path length is true path length for a very small step
0109         return gstep;
0110     }
0111 
0112     real_type tstep = [&] {
0113         if (alpha_ == MscStep::small_step_alpha())
0114         {
0115             // Cross section was assumed to be constant over the step:
0116             // z = lambda * (1 - exp(-g / lambda))
0117             // => g = -lambda * log(1 - g / lambda)
0118             real_type tstep = -lambda_ * std::log1p(-gstep / lambda_);
0119             if (tstep < params_.min_step())
0120             {
0121                 // Geometrical path length = true path length for a very small
0122                 // step
0123                 return gstep;
0124             }
0125             return tstep;
0126         }
0127 
0128         real_type w = 1 + 1 / (alpha_ * lambda_);
0129         real_type x = alpha_ * w * gstep;  // = (1 - (1 - alpha * true)^w)
0130         // Range-limited step results in x = 1, which gives the correct
0131         // result in the inverted equation below for alpha = 1/range.
0132         // x >= 1 corresponds to the stopping MFP being <= 0: x=1 meaning
0133         // ending MFP of zero is correct when the step is the range. Precision
0134         // loss means this conversion also may result in x > 1, which we guard
0135         // against.
0136         x = min(x, real_type(1));
0137         // Near x=1, (1 - (1-x)^(1/w)) suffers from numerical precision loss;
0138         // the maximum value of the expression should be range_.
0139         // TODO: we should use the action ID to avoid applying this
0140         // transformation if not range-limited.
0141         real_type temp = 1 - fastpow(1 - x, 1 / w);
0142         real_type result = temp / alpha_;
0143         return min(result, range_);
0144     }();
0145 
0146     // The result can be no less than the geometry path (effectively no
0147     // scattering took place) and no more than the original calculated true
0148     // path (if the step is path-limited).
0149     // The result can be slightly outside the bounds due to numerical
0150     // imprecision and edge cases:
0151     // - a few ULP due to inexactness of log1p/expm1
0152     // - small travel distance results in roundoff error inside (1 -
0153     //   alpha/true)^w
0154     // - gstep is just above min_step so that the updated recalculation of
0155     //   tstep is just below min_step
0156     return clamp(tstep, gstep, true_step_);
0157 }
0158 
0159 //---------------------------------------------------------------------------//
0160 }  // namespace detail
0161 }  // namespace celeritas