Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-06 10:15:19

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