Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:54:08

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 corecel/math/Quantity.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <type_traits>
0010 
0011 #include "corecel/Macros.hh"
0012 #include "corecel/Types.hh"
0013 
0014 #include "detail/QuantityImpl.hh"
0015 
0016 namespace celeritas
0017 {
0018 //---------------------------------------------------------------------------//
0019 /*!
0020  * A numerical value tagged with a unit.
0021  * \tparam UnitT  unit tag class
0022  * \tparam ValueT value type
0023  *
0024  * A quantity is a value expressed in terms of the given unit. Storing values
0025  * in a different unit system can help with some calculations (e.g. operating
0026  * in natural unit systems) by avoiding numerical multiplications and divisions
0027  * by large constants. It can also make debugging easier (numeric values are
0028  * obvious).
0029  *
0030  * Example usage by physics class, where charge is in units of \f$ q_{e^+} \f$,
0031  * and mass and momentum are expressed in atomic natural units (where
0032  * \f$ m_e = 1 \f$ and \f$ c = 1 \f$).
0033  * \code
0034    using MevEnergy   = Quantity<Mev, real_type>;
0035    using MevMass     = RealQuantity<UnitDivide<Mev, CLightSq>>;
0036    using MevMomentum = RealQuantity<UnitDivide<Mev, CLight>>;
0037    \endcode
0038  *
0039  * Note the use of the \c RealQuantity type alias (below).
0040  *
0041  * A relativistic equation that operates on these quantities can do so without
0042  * unnecessary floating point operations involving the speed of light:
0043  * \code
0044    real_type e_mev = value_as<MevEnergy>(energy); // Natural units
0045    MevMomentum momentum{std::sqrt(e_mev * e_mev
0046                                   + 2 * value_as<MevMass>(mass) * e_mev)};
0047    \endcode
0048  * The resulting quantity can be converted to the native Celeritas unit system
0049  * with `native_value_from`, which multiplies in the constant value of
0050  * ElMomentumUnit:
0051  * \code
0052    real_type mom = native_value_from(momentum);
0053  * \endcode
0054  *
0055  * When using a Quantity from another part of the code, e.g. an imported unit
0056  * system, use the \c value_as free function rather than \c .value() in order
0057  * to guarantee consistency of units between source and destination.
0058  *
0059  * An example unit class would be:
0060  * \code
0061     struct DozenUnit
0062     {
0063         static constexpr int value() { return 12; }
0064         static constexpr char const* label() { return "dozen"; }
0065     };
0066    \endcode
0067  *
0068  * The label is used solely for diagnostic purposes.
0069  *
0070  * \note The Quantity is designed to be a simple "strong type" class, not a
0071  * complex mathematical class. To operate on quantities, you must use
0072  * `value_as`
0073  * (to operate within the Quantity's unit system) or `native_value_from` (to
0074  * operate in the Celeritas native unit system), use the resulting numeric
0075  * values in your mathematical expressions, then return a new Quantity class
0076  * with the resulting value and correct type.
0077  */
0078 template<class UnitT, class ValueT>
0079 class Quantity
0080 {
0081   public:
0082     //!@{
0083     //! \name Type aliases
0084     using value_type = ValueT;
0085     using unit_type = UnitT;
0086     using unit_value_type = decltype(UnitT::value());
0087     using common_type = decltype(std::declval<value_type>()
0088                                  * std::declval<unit_value_type>());
0089     //!@}
0090 
0091   public:
0092     //! Construct with default (zero)
0093     constexpr Quantity() = default;
0094 
0095     //! Construct with value in celeritas native units
0096     explicit CELER_CONSTEXPR_FUNCTION Quantity(value_type value) noexcept
0097         : value_(value)
0098     {
0099     }
0100 
0101     //! Construct implicitly from a unitless quantity
0102     template<detail::QConstant QC>
0103     CELER_CONSTEXPR_FUNCTION Quantity(detail::UnitlessQuantity<QC>) noexcept
0104         : value_(detail::get_constant<ValueT>(QC))
0105     {
0106     }
0107 
0108     //!@{
0109     //! Access the underlying numeric value, discarding units
0110     CELER_CONSTEXPR_FUNCTION value_type& value() & noexcept { return value_; }
0111     CELER_CONSTEXPR_FUNCTION value_type const& value() const& noexcept
0112     {
0113         return value_;
0114     }
0115     //!@}
0116 
0117     //! Access the underlying data for more efficient loading from memory
0118     CELER_CONSTEXPR_FUNCTION value_type const* data() const { return &value_; }
0119 
0120   private:
0121     value_type value_{};
0122 };
0123 //---------------------------------------------------------------------------//
0124 
0125 //! Type alias for a quantity that uses compile-time precision
0126 template<class UnitT>
0127 using RealQuantity = Quantity<UnitT, real_type>;
0128 
0129 //---------------------------------------------------------------------------//
0130 //! \cond
0131 #define CELER_DEFINE_QUANTITY_CMP(TOKEN)                           \
0132     template<class U, class T, class T2>                           \
0133     CELER_CONSTEXPR_FUNCTION bool operator TOKEN(                  \
0134         Quantity<U, T> lhs, Quantity<U, T2> rhs) noexcept          \
0135     {                                                              \
0136         return lhs.value() TOKEN rhs.value();                      \
0137     }                                                              \
0138     template<class U, class T, detail::QConstant QC>               \
0139     CELER_CONSTEXPR_FUNCTION bool operator TOKEN(                  \
0140         Quantity<U, T> lhs, detail::UnitlessQuantity<QC>) noexcept \
0141     {                                                              \
0142         return lhs.value() TOKEN detail::get_constant<T>(QC);      \
0143     }                                                              \
0144     template<class U, class T, detail::QConstant QC>               \
0145     CELER_CONSTEXPR_FUNCTION bool operator TOKEN(                  \
0146         detail::UnitlessQuantity<QC>, Quantity<U, T> rhs) noexcept \
0147     {                                                              \
0148         return detail::get_constant<T>(QC) TOKEN rhs.value();      \
0149     }                                                              \
0150     namespace detail                                               \
0151     {                                                              \
0152     template<detail::QConstant C1, detail::QConstant C2>           \
0153     CELER_CONSTEXPR_FUNCTION bool                                  \
0154     operator TOKEN(detail::UnitlessQuantity<C1>,                   \
0155                    detail::UnitlessQuantity<C2>) noexcept          \
0156     {                                                              \
0157         return static_cast<int>(C1) TOKEN static_cast<int>(C2);    \
0158     }                                                              \
0159     }
0160 
0161 //!@{
0162 //! Comparison for Quantity
0163 CELER_DEFINE_QUANTITY_CMP(==)
0164 CELER_DEFINE_QUANTITY_CMP(!=)
0165 CELER_DEFINE_QUANTITY_CMP(<)
0166 CELER_DEFINE_QUANTITY_CMP(>)
0167 CELER_DEFINE_QUANTITY_CMP(<=)
0168 CELER_DEFINE_QUANTITY_CMP(>=)
0169 //!@}
0170 
0171 #undef CELER_DEFINE_QUANTITY_CMP
0172 
0173 //!@{
0174 //! Math operator for Quantity
0175 template<class U, class T, class T2>
0176 CELER_CONSTEXPR_FUNCTION auto
0177 operator+(Quantity<U, T> lhs, Quantity<U, T2> rhs) noexcept
0178 {
0179     return Quantity<U, std::common_type_t<T, T2>>{lhs.value() + rhs.value()};
0180 }
0181 
0182 template<class U, class T, class T2>
0183 CELER_CONSTEXPR_FUNCTION auto
0184 operator-(Quantity<U, T> lhs, Quantity<U, T2> rhs) noexcept
0185 {
0186     return Quantity<U, std::common_type_t<T, T2>>{lhs.value() - rhs.value()};
0187 }
0188 
0189 template<class U, class T, class T2>
0190 CELER_CONSTEXPR_FUNCTION auto
0191 operator/(Quantity<U, T> lhs, Quantity<U, T2> rhs) noexcept
0192 {
0193     return lhs.value() / rhs.value();
0194 }
0195 
0196 template<class U, class T>
0197 CELER_CONSTEXPR_FUNCTION auto operator-(Quantity<U, T> q) noexcept
0198 {
0199     return Quantity<U, T>{-q.value()};
0200 }
0201 
0202 template<class U, class T, class T2>
0203 CELER_CONSTEXPR_FUNCTION auto operator*(Quantity<U, T> lhs, T2 rhs) noexcept
0204 {
0205     return Quantity<U, std::common_type_t<T, T2>>{lhs.value() * rhs};
0206 }
0207 
0208 template<class T, class U, class T2>
0209 CELER_CONSTEXPR_FUNCTION auto operator*(T rhs, Quantity<U, T2> lhs) noexcept
0210 {
0211     return Quantity<U, std::common_type_t<T, T2>>{rhs * lhs.value()};
0212 }
0213 
0214 template<class U, class T, class T2>
0215 CELER_CONSTEXPR_FUNCTION auto operator/(Quantity<U, T> lhs, T2 rhs) noexcept
0216 {
0217     return Quantity<U, std::common_type_t<T, T2>>{lhs.value() / rhs};
0218 }
0219 //!@!}
0220 
0221 //! \endcond
0222 //---------------------------------------------------------------------------//
0223 // FREE FUNCTIONS
0224 //---------------------------------------------------------------------------//
0225 /*!
0226  * Get a typeless zero quantity.
0227  *
0228  * The zero quantity can be compared against any Quantity.
0229  */
0230 CELER_CONSTEXPR_FUNCTION auto zero_quantity() noexcept
0231 {
0232     return detail::UnitlessQuantity<detail::QConstant::zero>{};
0233 }
0234 
0235 //---------------------------------------------------------------------------//
0236 /*!
0237  * Get a typeless quantitity greater than any other numeric quantity.
0238  */
0239 CELER_CONSTEXPR_FUNCTION auto max_quantity() noexcept
0240 {
0241     return detail::UnitlessQuantity<detail::QConstant::max>{};
0242 }
0243 
0244 //---------------------------------------------------------------------------//
0245 /*!
0246  * Get a quantitity less than any other numeric quantity.
0247  */
0248 CELER_CONSTEXPR_FUNCTION auto neg_max_quantity() noexcept
0249 {
0250     return detail::UnitlessQuantity<detail::QConstant::neg_max>{};
0251 }
0252 
0253 //---------------------------------------------------------------------------//
0254 /*!
0255  * Swap two Quantities.
0256  */
0257 template<class U, class V>
0258 CELER_CONSTEXPR_FUNCTION void
0259 swap(Quantity<U, V>& a, Quantity<U, V>& b) noexcept
0260 {
0261     Quantity<U, V> tmp{a};
0262     a = b;
0263     b = tmp;
0264 }
0265 
0266 //---------------------------------------------------------------------------//
0267 /*!
0268  * Convert the given quantity into the native Celeritas unit system.
0269  *
0270  * \code
0271    assert(native_value_from(Quantity<CLight>{1}) == 2.998e10 *
0272    centimeter/second);
0273  * \endcode
0274  */
0275 template<class UnitT, class ValueT>
0276 CELER_CONSTEXPR_FUNCTION auto
0277 native_value_from(Quantity<UnitT, ValueT> quant) noexcept
0278 {
0279     using common_type = typename Quantity<UnitT, ValueT>::common_type;
0280     return static_cast<common_type>(quant.value())
0281            * static_cast<common_type>(UnitT::value());
0282 }
0283 
0284 //---------------------------------------------------------------------------//
0285 /*!
0286  * Create a quantity from a value in the Celeritas unit system.
0287  *
0288  * This function can be used for defining a constant for use in another unit
0289  * system (typically a "natural" unit system for use in physics kernels).
0290  *
0291  * An extra cast may be needed when mixing \c float, \c double, and \c
0292  * celeritas::Constant.
0293  */
0294 template<class Q, class T>
0295 CELER_CONSTEXPR_FUNCTION Q native_value_to(T value) noexcept
0296 {
0297     using common_type = typename Q::common_type;
0298     using value_type = typename Q::value_type;
0299     constexpr auto unit_value = Q::unit_type::value();
0300     return Q{
0301         static_cast<value_type>(value / static_cast<common_type>(unit_value))};
0302 }
0303 
0304 //---------------------------------------------------------------------------//
0305 /*!
0306  * Use the value of a Quantity.
0307  *
0308  * The redundant unit type in the function signature is to make coupling safer
0309  * across different parts of the code and to make the user code more readable.
0310  *
0311  * \code
0312  assert(value_as<LightSpeed>(LightSpeed{1}) == 1);
0313  * \endcode
0314  */
0315 template<class Q, class SrcUnitT, class ValueT>
0316 CELER_CONSTEXPR_FUNCTION auto
0317 value_as(Quantity<SrcUnitT, ValueT> quant) noexcept -> ValueT
0318 {
0319     static_assert(std::is_same<Q, Quantity<SrcUnitT, ValueT>>::value,
0320                   "quantity units do not match");
0321     return quant.value();
0322 }
0323 
0324 //---------------------------------------------------------------------------//
0325 /*!
0326  * Get the label for a unit returned from a class accessor.
0327  *
0328  * Example:
0329  * \code
0330    cout << accessor_unit_label<decltype(&ParticleView::mass)>() << endl;
0331    \endcode
0332  */
0333 template<class T>
0334 inline char const* accessor_unit_label()
0335 {
0336     return detail::AccessorResultType<T>::unit_type::label();
0337 }
0338 
0339 //---------------------------------------------------------------------------//
0340 }  // namespace celeritas