Units and constants

The default unit system in Celeritas is CGS with centimeter (cm), gram (g), second (s), gauss (G), and kelvin (K) all having a value of unity. With these definitions, densities can be defined in natural units of \(\mathrm{g}/\mathrm{cm}^3\), and macroscopic cross sections are in units of \(\mathrm{cm}^{-1}\). See the units documentation for more descriptions of the core unit system and the exactly defined values for SI units such as tesla.

Celeritas defines constants from a few different sources. Mathematical constants are defined as truncated floating point values. Some physical constants such as the speed of light, Planck’s constant, and the electron charge, have exact numerical values as specified by the SI unit system [Bureau International des Poids et Mesures, 2019]. Other physical constants such as the atomic mass unit and electron radius are derived from experimental measurements in CODATA 2018 [Tiesinga et al., 2021]. Because the reported constants are derived from regression fits to experimental data points, some exactly defined physical relationships (such as the fine structure constant \(\alpha = \frac{e^2}{2 \epsilon_0 h c}\)) are only approximate.

Unlike Geant4 and the CLHEP unit systems [Lönnblad, 1994], Celeritas avoids using “natural” units in its definitions. Although a natural unit system makes some expressions easier to evaluate, it can lead to errors in the definition of derivative constants and inconsistencies with other macro-scale unit systems such as SI. To harmonize special unit systems with the native Celeritas unit system, the Quantity class stores quantities in another unit system with a compile-time constant that allows their conversion back to native units. This allows, for example, particles to represent their energy as MeV and charge as fractions of e but work seamlessly with a field definition in native (macro-scale quantity) units.

Quantity

Celeritas supports multiple simultaneous unit systems (e.g., atomic scale/natural units working with a macro-scale but consistent unit system) using the Quantity class and helper functions.

template<class UnitT, class ValueT>
class Quantity

A numerical value tagged with a unit.

A quantity is a value expressed in terms of the given unit. Storing values in a different unit system can help with some calculations (e.g. operating in natural unit systems) by avoiding numerical multiplications and divisions by large constants. It can also make debugging easier (numeric values are obvious).

Example usage by physics class, where charge is in units of \( q_{e^+} \), and mass and momentum are expressed in atomic natural units (where \( m_e = 1 \) and \( c = 1 \)).

using MevEnergy   = Quantity<Mev, real_type>;
using MevMass     = RealQuantity<UnitDivide<Mev, CLightSq>>;
using MevMomentum = RealQuantity<UnitDivide<Mev, CLight>>;

Note the use of the RealQuantity type alias (below).

A relativistic equation that operates on these quantities can do so without unnecessary floating point operations involving the speed of light:

real_type e_mev = value_as<MevEnergy>(energy); // Natural units
MevMomentum momentum{std::sqrt(e_mev * e_mev
                               + 2 * value_as<MevMass>(mass) * e_mev)};
The resulting quantity can be converted to the native Celeritas unit system with native_value_from, which multiplies in the constant value of ElMomentumUnit:
real_type mom = native_value_from(momentum);

When using a Quantity from another part of the code, e.g. an imported unit system, use the value_as free function rather than .value() in order to guarantee consistency of units between source and destination.

An example unit class would be:

struct DozenUnit
{
    static constexpr int value() { return 12; }
    static constexpr char const* label() { return "dozen"; }
};

The label is used solely for diagnostic purposes.

Note

The Quantity is designed to be a simple “strong type” class, not a complex mathematical class. To operate on quantities, you must use value_as (to operate within the Quantity’s unit system) or native_value_from (to operate in the Celeritas native unit system), use the resulting numeric values in your mathematical expressions, then return a new Quantity class with the resulting value and correct type.

Template Parameters:
  • UnitT – unit tag class

  • ValueT – value type

template<class UnitT>
using celeritas::RealQuantity = Quantity<UnitT, real_type>

Type alias for a quantity that uses compile-time precision.

template<class Q, class T>
Q celeritas::native_value_to(T value) noexcept

Create a quantity from a value in the Celeritas unit system.

This function can be used for defining a constant for use in another unit system (typically a “natural” unit system for use in physics kernels).

An extra cast may be needed when mixing float, double, and celeritas::Constant.

template<class UnitT, class ValueT>
auto celeritas::native_value_from(Quantity<UnitT, ValueT> quant) noexcept

Convert the given quantity into the native Celeritas unit system.

assert(native_value_from(Quantity<CLight>{1}) == 2.998e10 *
centimeter/second);
template<class Q, class SrcUnitT, class ValueT>
auto celeritas::value_as(Quantity<SrcUnitT, ValueT> quant) noexcept -> ValueT

Use the value of a Quantity.

The redundant unit type in the function signature is to make coupling safer across different parts of the code and to make the user code more readable.

assert(value_as<LightSpeed>(LightSpeed{1}) == 1);
auto celeritas::zero_quantity() noexcept

Get a typeless zero quantity.

The zero quantity can be compared against any Quantity.

auto celeritas::max_quantity() noexcept

Get a typeless quantitity greater than any other numeric quantity.

auto celeritas::neg_max_quantity() noexcept

Get a quantitity less than any other numeric quantity.

An example quantity uses \(2\pi\) as the unit type to allow integral values for turns. Using a Quantity also allows us to override the sincos function to use sincospi under the hood for improved precision.

using celeritas::Turn = Quantity<TwoPi, real_type>

Quantity denoting a full turn.

Turns are a useful way of representing angles without the historical arbitrariness of degrees or the roundoff errors of radians. See, for example, https://www.computerenhance.com/p/turns-are-better-than-radians .

Todo:

Template on real type and template the functions below.

void celeritas::sincos(Turn r, real_type *sinv, real_type *cosv)

Special overrides for math functions for more precise arithmetic

Units

namespace units

Units in Celeritas for macro-scale quantities.

Celeritas can be configured at build time to use different unit systems for better compatibility with external libraries and applications. The CELERITAS_UNITS CMake variable can be set to one of the following:

  • CELERITAS_UNITS_CGS (default): use Gaussian CGS units

  • CELERITAS_UNITS_SI: use SI units

  • CELERITAS_UNITS_CLHEP: use the Geant4 high energy physics system (a mix of macro-scale and atomic-scale units)

The following units have numerical values of 1 in the default Celeritas system (Gaussian CGS) and are often seen in unit tests:

  • cm for standard unit of length

  • s for standard unit of time

  • g for standard unit of mass

  • G for standard unit of field strength

Unless otherwise specified, the user-selected unit system is used for input and output numerical values. They are meant for macro-scale quantities coupling the different code components of Celeritas.

See also:

  • Constants.hh for constants defined in this unit system

  • physics/base/Units.hh for unit systems used by the physics

Additionally:

  • radians are used for measures of angle (unitless)

  • steradians are used for measures of solid angle (unitless)

Note

This system of units should be fully consistent so that constants can be precisely defined. (E.g., you cannot define both MeV as 1 and Joule as 1.) To express quantities in another system of units, such as MeV and “natural” units, use the Quantity class.

Quantities for atomic scale/natural units

using ElementaryCharge = RealQuantity<EElectron>
using MevEnergy = RealQuantity<Mev>
using MevMass = RealQuantity<MevPerCsq>
using MevMomentum = RealQuantity<MevPerC>
using MevMomentumSq = RealQuantity<UnitProduct<MevPerC, MevPerC>>
using LightSpeed = RealQuantity<CLight>
using AmuMass = RealQuantity<Amu>

Quantities for manual input and/or test harnesses

using BarnXs = RealQuantity<Barn>
using CmLength = RealQuantity<Centimeter>
using InvCmXs = RealQuantity<UnitInverse<Centimeter>>
using InvCcDensity = RealQuantity<InvCentimeterCubed>
using MolCcDensity = RealQuantity<MolPerCentimeterCubed>
using GramCcDensity = RealQuantity<GramPerCentimeterCubed>
using FieldTesla = RealQuantity<Tesla>

Units with numerical value defined to be 1 for CGS

constexpr Constant centimeter = {1}

Length.

constexpr Constant gram = {1}

Mass.

constexpr Constant second = {1}

Time.

constexpr Constant gauss = {1}

Field strength.

constexpr Constant kelvin = {1}

Temperature.

Exact unit transformations to SI units

constexpr Constant meter = 100 * centimeter
constexpr Constant kilogram = 1000 * gram
constexpr Constant tesla = 10000 * gauss

Exact unit transformations using SI unit definitions

constexpr Constant newton = kilogram * meter / (second * second)
constexpr Constant joule = newton * meter
constexpr Constant coulomb = kilogram / (tesla * second)
constexpr Constant ampere = coulomb / second
constexpr Constant volt = joule / coulomb
constexpr Constant farad = coulomb / volt

CLHEP units

constexpr Constant millimeter = Constant{0.1} * centimeter
constexpr Constant nanosecond = Constant{1e-9} * second

Other common units

constexpr Constant micrometer = Constant{1e-4} * centimeter
constexpr Constant nanometer = Constant{1e-7} * centimeter
constexpr Constant femtometer = Constant{1e-13} * centimeter
constexpr Constant barn = Constant{1e-24} * centimeter * centimeter

Typedefs

using LogMevEnergy = RealQuantity<LogMev>

Special faux quantity for overloading cross section calculation.

Constants

class Constant

Full-precision floating point constant with automatic precision demotion.

We want two behaviors from constants in Celeritas:

  1. They don’t accidentally promote runtime arithmetic from single to double precision when compiling at a lower precision. This incurs a substantial performance penalty on GPU.

  2. We can use their full double-precision values when we need to: either in templated code or when interacting with other libraries. (For example, float(pi) > pi which can lead to errors in Geant4.)

This class stores a full-precision (double) value as its “real type” and defines explicit conversion operators that allow it to automatically convert to a lower-precision or real-precision type.

Operations with a floating point value returns a value of that precision (performed at that precision level); operations with integers return a full-precision Constant; and operations with Constants return a Constant.

namespace constants

Mathematical, numerical, and physical constants.

Some of the physical constants listed here are exact numerical values: see Bureau International des Poids et Mesures [2019] for definition of constants and how they relate to the different units.

Celeritas

CLHEP

Notes

a0_bohr

Bohr_radius

Bohr radius

alpha_fine_structure

fine_structure_const

atomic_mass

amu

Not the same as 1/avogadro

eps_electric

epsilon0

Vacuum permittivity

h_planck

h_Planck

k_boltzmann

k_Boltzmann

mu_magnetic

mu0

Vacuum permeability

na_avogadro

Avogadro

[1/mol]

r_electron

classic_electr_radius

Classical electron radius

kcd_luminous

[none]

Lumens per Watt

lambdabar_electron

electron_Compton_length

Reduced Compton wavelength

stable_decay_constant

[none]

Decay for a stable particle

In the CLHEP unit system, the value of the constant e_electron is defined to be 1 and coulomb is derivative from that. To avoid floating point arithmetic issues that would lead to the “units” and “constants” having different values for it, a special case redefines the value for CLHEP.

Some experimental physical constants are derived from the other physical constants, but for consistency and clarity they are presented numerically with the units provided in the CODATA 2018 dataset. The Constants.test.cc unit tests compare the numerical value against the derivative values inside the celeritas unit system. All experimental values include the final (ususally two) imprecise digits; their precision is usually on the order of \( 10^{-11} \).

Physical constants with exact value as defined by SI

constexpr Constant c_light = Constant{299792458.} * units::meter / units::second
constexpr Constant h_planck = Constant{6.62607015e-34} * units::joule * units::second
constexpr Constant k_boltzmann = Constant{1.380649e-23} * units::joule / units::kelvin
constexpr Constant na_avogadro = {6.02214076e23}
constexpr Constant kcd_luminous = {683}

Exact derivative constants

constexpr Constant hbar_planck = {h_planck / (2 * pi)}

Experimental physical constants from CODATA 2018

constexpr Constant a0_bohr = Constant{5.29177210903e-11} * units::meter
constexpr Constant alpha_fine_structure = Constant{7.2973525693e-3}
constexpr Constant atomic_mass = Constant{1.66053906660e-24} * units::gram
constexpr Constant electron_mass = Constant{9.1093837015e-28} * units::gram
constexpr Constant proton_mass = Constant{1.67262192369e-24} * units::gram
constexpr Constant eps_electric = Constant{8.8541878128e-12} * units::farad / units::meter
constexpr Constant mu_magnetic = Constant{1.25663706212e-6} * units::newton / (units::ampere * units::ampere)
constexpr Constant r_electron = Constant{2.8179403262e-15} * units::meter
constexpr Constant rinf_rydberg = Constant{10973731.568160} / units::meter
constexpr Constant eh_hartree = Constant{4.3597447222071e-18} / units::meter
constexpr Constant lambdabar_electron = Constant{3.8615926796e-13} * units::meter

Other constants

constexpr int stable_decay_constant = {0}

Mathemetical constants (truncated)

constexpr Constant pi = {3.14159265358979323846}
constexpr Constant sqrt_pi = {1.77245385090551602730}
constexpr Constant euler = {2.71828182845904523536}
constexpr Constant sqrt_euler = {1.64872127070012814685}
constexpr Constant sqrt_two = {1.41421356237309504880}
constexpr Constant sqrt_three = {1.73205080756887729353}

Variables

constexpr Constant e_electron = {1}

Special case for CLHEP: electron charged is unity by definition.