Units and quantities

Internally, wave_tracer uses a strongly-typed unit system for all physical quantities. This is very useful when writing physical simulation software, and helps catch errors early. Scene files also use units for quantities.

wave_tracer defines wt::f_t as the floating-point representation type for real numbers. wt::f_t to the single-precision 32-bit float, but can be compile-time configured.

wave_tracer uses the mp-units library for strongly-typed units and dimensions. Units are defined in namespace wt::u. The underlying numeric representation for all these quantities is wt::f_t. In addition, common types for quantities, like length, wavenumber and power, are defined (see below). For example,

length_t len = 3 * u::mm;
area_t area = m::sqr(len);
auto p = irradiance_t(1.1f * u::W / area);
std::cout << std::format("{}", p) << std::endl;

defines a variable with a quantity concept of ISQ length with units of millimetre, squares it to produce area, and then constructs a quantity of radiometric irradiance. length_t stores values in units of metres. Dimensionless quantities can be cast to a unitless number wt::f_t in two ways:

auto area_ratio = area / (.5f * u::m2);
std::cout << std::format("{}", (f_t)area_ratio) << std::endl;
std::cout << std::format("{}", u::to_num(area_ratio)) << std::endl;

The static cast guarantees a lossless conversion. At times a lossless conversion is not possible, and the static cast will fail to compile:

auto area_ratio = area / m::sqr(1 * u::mm);
std::cout << std::format("{}", (f_t)area_ratio) << std::endl;        // fails to compile!
std::cout << std::format("{}", u::to_num(area_ratio)) << std::endl;  // compiles: forces lossy conversion

The former is not a lossless conversion because the type area_t uses units of metre squared; therefore, even though the type of area_ratio is a dimensionless quantity, its units include a static scaling constant of \(\tfrac{\text{m}^2}{\text{mm}^2}\), and a conversion to a unitless number cannot be done losslessly. The explicit cast via wt::u::to_num() forces a lossy conversion.

Neither form will compile when attempting to convert from a non-dimensionless quantity. To cast units away, different functions of the form wt::u::to_X are provided. For example,

length_t len = 3 * u::mm;
std::cout << std::format("len = {} mm", u::to_mm(1 * u::m)) << std::endl;
std::cout << std::format("len = {} m", u::to_m(len)) << std::endl;
std::cout << std::format("{}", u::to_Hz(1 * u::m)) << std::endl;   // fails to compile: cannot cast `mm` to `Hz`

The first cast is lossless, and simply discards the units to produce a number (of type wt::f_t); the second cast is lossy, and first converts the representation of len to metres; the third cast does not compile as a conversion from millimetres to Hertz is not legal.

Quantities of same concept can be compared to each other and ordered:

static_assert(1 * u::mm < 1 * u::m);

Comparisons between quantities of different concepts will fail to compile. Common math operations, like wt::m::sqrt(), wt::m::abs(), wt::m::inverse() are defined for quantities and behave properly. Likewise, trigonometric functions are defined for angle quantities. c++ concepts for different quantity concepts are also defined. For example, the following template function accepts any angle quantities, with no runtime conversions:

auto cone_solid_angle(const Angle auto& half_opening_angle) {
   return m::four_pi * m::sqr(m::sin(half_opening_angle / 2.f));
}

std::cout << std::format("{}", cone_solid_angle(1 * u::ang::rad)) << std::endl;
std::cout << std::format("{}", cone_solid_angle(m::pi/180 * u::ang::deg)) << std::endl;

General quantities

The following general quantities are defined. The suffix “[unit]” indicates the units used for internal storage.

length

wt::length_t \([\small\text{m}]\)

area

wt::area_t \([\small\text{m}^2]\)

volume

wt::volume_t \([\small\text{m}^3]\)

length density

wt::length_density_t \([\small\text{m}^{-1}]\)

area density

wt::area_density_t \([\small\text{m}^{-2}]\)

volume density

wt::volume_density_t \([\small\text{m}^{-3}]\)

angle

wt::angle_t \([\small\text{radians}]\)

solid angle

wt::solid_angle_t \([\small\text{sr}]\)

angle density

wt::angle_density_t \([\small\text{radians}^{-1}]\)

solid angle density

wt::solid_angle_density_t \([\small\text{sr}^{-1}]\)

wavenumber

wt::wavenumber_t \([\small\text{mm}^{-1}]\)

wavelength

wt::wavelength_t \([\small\text{mm}]\)

frequency

wt::frequency_t \([\small\text{GHz}]\)

wavenumber density

wt::wavenumber_density_t \([\small\text{mm}]\)

wavelength density

wt::wavelength_density_t \([\small\text{mm}^{-1}]\)

power

wt::power_t \([\text{Watt}]\)

temperature

wt::temperature_t \([\text{K}]\)

As long as the quantity concepts match, a quantity can be implicitly converted to another. For example: f_t(1) / length_t(2 * u::m) can be implicitly captured by a wt::length_density_t, and vice versa.

Some helper converters for quantities that quantify electrodynamic (EM) radiation frequency are defined:

wavelength to wavenumber

wt::wavelen_to_wavenum()

wavenumber to wavelength

wt::wavenum_to_wavelen()

EM frequency to wavelength (vacuum)

wt::freq_to_wavelen()

EM frequency to wavenumber (vacuum)

wt::freq_to_wavenum()

wavenumber (vacuum) to EM frequency

wt::wavenum_to_freq()

Radiometric quantities

wave_tracer defines common power-derived radiometric quantities, including spectral variants. Note that wave_tracer defines spectral quantities per wavenumber, and not per wavelength.

radiated flux

wt::radiant_flux_t \([\small\text{Watt}]\)

radiant intensity

wt::radiant_intensity_t \([\tfrac{\text{Watt}}{\text{sr}}]\)

radiated irradiance

wt::irradiance_t \([\tfrac{\text{Watt}}{\text{m}^2}]\)

radiance

wt::radiance_t \([\tfrac{\text{Watt}}{\text{m}^2 \cdot \text{sr}}]\)

spectral radiated flux

wt::spectral_radiant_flux_t \([\small\text{mm}\cdot\text{Watt}]\)

spectral radiant intensity

wt::spectral_radiant_intensity_t \([\tfrac{\text{mm}\cdot\text{Watt}}{\text{sr}}]\)

spectral irradiance

wt::spectral_irradiance_t \([\tfrac{\text{mm}\cdot\text{Watt}}{\text{m}^2}]\)

spectral radiance

wt::spectral_radiance_t \([\tfrac{\text{mm}\cdot\text{Watt}}{\text{m}^2 \cdot \text{sr}}]\)

Importance-derived quantities are also defined:

importance flux

wt::QE_flux_t \([\small\text{m}^2 \cdot \text{sr}]\)

importance flux per solid angle

wt::QE_area_t \([\small\text{m}^2]\)

importance flux per area

wt::QE_solid_angle_t \([\small\text{sr}]\)

importance (Quantum efficiency)

wt::QE_t \([\small\text{dimensionless}]\)

The importance-derived quantities have a similar geometric meaning to the power-derived quantities: for example, spectral radiated flux and importance flux are both quantities that integrate emitted power or importance, respectively, over solid angle and area; spectral irradiance and importance flux per area are both differential quantities that quantify flux (power or importance, respectively) per unit area.

Non-spectral variants of importance-derived quantities above are not defined, as these are not used in practice.

Facilities that are used for light transport, like Stokes parameters vectors and beams (the primary light transport primitive), have specializations for relevant radiometric quantities. For example, wt::spectral_radiance_stokes_t and wt::spectral_radiance_beam_t.

Units in scene file

WIP