2023-06-29 15:09:38 +01:00
|
|
|
# Systems of Units
|
|
|
|
|
|
2023-08-03 21:23:34 +02:00
|
|
|
Modeling a [system of units](../../appendix/glossary.md#system-of-units) is probably
|
2023-06-29 15:09:38 +01:00
|
|
|
the most important feature and a selling point of every physical units library.
|
|
|
|
|
Thanks to that, the library can protect users from performing invalid operations on
|
|
|
|
|
quantities and provide automated conversion factors between various compatible units.
|
|
|
|
|
|
2023-08-03 21:23:34 +02:00
|
|
|
Probably all the libraries in the wild model the [SI](../../appendix/glossary.md#si)
|
2023-06-29 15:09:38 +01:00
|
|
|
and many of them provide support for additional units belonging to various other systems
|
2023-11-06 21:55:44 -10:00
|
|
|
(e.g., imperial, cgs, etc).
|
2023-06-29 15:09:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Systems of Units are based on Systems of Quantities
|
|
|
|
|
|
2023-08-03 21:23:34 +02:00
|
|
|
[Systems of quantities](../../appendix/glossary.md#system-of-quantities) specify a set
|
2023-06-29 15:09:38 +01:00
|
|
|
of quantities and equations relating to those quantities. Those equations do not take any
|
2023-11-06 21:55:44 -10:00
|
|
|
unit or a numerical representation into account at all. To create a quantity,
|
2023-06-29 15:09:38 +01:00
|
|
|
we need to add those missing pieces of information. This is where
|
2023-08-03 21:23:34 +02:00
|
|
|
a [system of units](../../appendix/glossary.md#system-of-units) kicks in.
|
2023-06-29 15:09:38 +01:00
|
|
|
|
2023-08-03 21:23:34 +02:00
|
|
|
The [SI](../../appendix/glossary.md#si) is explicitly stated to be based on
|
|
|
|
|
the [ISQ](../../appendix/glossary.md#isq). Among others, it defines
|
|
|
|
|
`7` [base units](../../appendix/glossary.md#base-unit), one for each
|
|
|
|
|
[base quantity](../../appendix/glossary.md#base-quantity). In the **mp-units**
|
2023-06-29 15:09:38 +01:00
|
|
|
this is expressed by associating a quantity kind (that we discussed in detail in the
|
|
|
|
|
previous chapter) with a unit that is used to express it:
|
|
|
|
|
|
|
|
|
|
```cpp
|
2024-09-05 10:06:43 +02:00
|
|
|
inline constexpr struct metre final : named_unit<"m", kind_of<isq::length>> {} metre;
|
2023-06-29 15:09:38 +01:00
|
|
|
```
|
|
|
|
|
|
2023-08-30 11:33:30 +02:00
|
|
|
!!! important
|
2023-06-29 15:09:38 +01:00
|
|
|
|
|
|
|
|
The `kind_of<isq::length>` above states explicitly that this unit has
|
|
|
|
|
an associated quantity kind. In other words, `si::metre` (and scaled units based
|
2023-11-06 21:55:44 -10:00
|
|
|
on it) can be used to express the amount of any quantity of kind _length_.
|
2023-06-29 15:09:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Units compose
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
One of the most vital points of the [SI](../../appendix/glossary.md#si) system
|
2023-06-29 15:09:38 +01:00
|
|
|
is that its units compose. This allows providing thousands of different units for
|
2023-11-06 21:55:44 -10:00
|
|
|
hundreds of various quantities with a tiny set of predefined units
|
2023-06-29 15:09:38 +01:00
|
|
|
and prefixes.
|
|
|
|
|
|
|
|
|
|
The same is modeled in the **mp-units** library, which also allows composing
|
|
|
|
|
predefined units to create a nearly infinite number of different
|
2023-08-03 21:23:34 +02:00
|
|
|
[derived units](../../appendix/glossary.md#derived-unit). For example, one can write:
|
2023-06-29 15:09:38 +01:00
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
quantity<si::metre / si::second> q;
|
|
|
|
|
```
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
to express a quantity of _speed_. The resulting quantity type is implicitly inferred
|
2023-08-03 21:23:34 +02:00
|
|
|
from the [unit equation](../../appendix/glossary.md#unit-equation) by repeating
|
2023-11-06 21:55:44 -10:00
|
|
|
the same operations on the associated quantity kinds.
|
2023-06-29 15:09:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Many shades of the same unit
|
|
|
|
|
|
2023-08-03 21:23:34 +02:00
|
|
|
The [SI](../../appendix/glossary.md#si) provides the names for 22 common
|
|
|
|
|
[coherent units](../../appendix/glossary.md#coherent-derived-unit) of 22
|
|
|
|
|
[derived quantities](../../appendix/glossary.md#derived-quantity).
|
2023-06-29 15:09:38 +01:00
|
|
|
|
2023-08-03 21:23:34 +02:00
|
|
|
Each such named [derived unit](../../appendix/glossary.md#derived-unit) is a result
|
|
|
|
|
of a specific predefined [unit equation](../../appendix/glossary.md#unit-equation).
|
2023-11-06 21:55:44 -10:00
|
|
|
For example, a unit of _power_ quantity is defined in the library as:
|
2023-06-29 15:09:38 +01:00
|
|
|
|
|
|
|
|
```cpp
|
2024-09-05 10:06:43 +02:00
|
|
|
inline constexpr struct watt final : named_unit<"W", joule / second> {} watt;
|
2023-06-29 15:09:38 +01:00
|
|
|
```
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
However, a _power_ quantity can be expressed in other units as well. For example,
|
2023-06-29 15:09:38 +01:00
|
|
|
the following:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
auto q1 = 42 * W;
|
|
|
|
|
std::cout << q1 << "\n";
|
2023-08-23 16:46:15 +02:00
|
|
|
std::cout << q1.in(J / s) << "\n";
|
|
|
|
|
std::cout << q1.in(N * m / s) << "\n";
|
|
|
|
|
std::cout << q1.in(kg * m2 / s3) << "\n";
|
2023-06-29 15:09:38 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
prints:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
42 W
|
|
|
|
|
42 J/s
|
|
|
|
|
42 N m/s
|
|
|
|
|
42 kg m²/s³
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
All of the above quantities are equivalent and mean exactly the same.
|
|
|
|
|
|
2024-10-16 17:18:19 +02:00
|
|
|
!!! note
|
|
|
|
|
|
|
|
|
|
The above code example may give the impression that the order of components in a derived
|
|
|
|
|
unit is determined by the multiplication order. This is not the case. As stated in
|
|
|
|
|
[Simplifying the resulting expression templates](interface_introduction.md#simplifying-the-resulting-expression-templates),
|
|
|
|
|
to be able to reason about and simplify units, the library needs to order them in an
|
|
|
|
|
appropriate order. This will affect the order of components in a resulting type and
|
|
|
|
|
text output.
|
|
|
|
|
|
|
|
|
|
Please refer to [our FAQ](../../getting_started/faq.md#why-derived-units-order-is-not-preserved-from-the-multiplication)
|
|
|
|
|
for more information.
|
|
|
|
|
|
2023-06-29 15:09:38 +01:00
|
|
|
|
|
|
|
|
## Constraining a derived unit to work only with a specific derived quantity
|
|
|
|
|
|
|
|
|
|
Some derived units are valid only for specific derived quantities. For example,
|
2023-11-06 21:55:44 -10:00
|
|
|
[SI](../../appendix/glossary.md#si) specifies both `hertz` and `becquerel` derived units
|
|
|
|
|
with the same unit equation `1 / s`. However, it also explicitly states:
|
2023-06-29 15:09:38 +01:00
|
|
|
|
|
|
|
|
!!! quote "SI Brochure"
|
|
|
|
|
|
|
|
|
|
The hertz shall only be used for periodic phenomena and the becquerel shall only be used for
|
|
|
|
|
stochastic processes in activity referred to a radionuclide.
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
The above means that the usage of `becquerel` as a unit of a _frequency_ quantity is an error.
|
|
|
|
|
|
|
|
|
|
The library allows constraining such units to work only with quantities of a specific kind in
|
|
|
|
|
the following way:
|
2023-06-29 15:09:38 +01:00
|
|
|
|
|
|
|
|
```cpp
|
2024-09-05 10:06:43 +02:00
|
|
|
inline constexpr struct hertz final : named_unit<"Hz", one / second, kind_of<isq::frequency>> {} hertz;
|
|
|
|
|
inline constexpr struct becquerel final : named_unit<"Bq", one / second, kind_of<isq::activity>> {} becquerel;
|
2023-06-29 15:09:38 +01:00
|
|
|
```
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
With the above, `hertz` can only be used with _frequencies_, while `becquerel` should only be used with
|
|
|
|
|
quantities of _activity_. This means that the following equation will not compile:
|
2023-06-29 15:09:38 +01:00
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
auto q = 1 * Hz + 1 * Bq; // Fails to compile
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This is exactly what we wanted to achieve to improve the type-safety of the library.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Prefixed units
|
|
|
|
|
|
2023-08-03 21:23:34 +02:00
|
|
|
Besides named units, the [SI](../../appendix/glossary.md#si) specifies also 24 prefixes
|
2023-06-29 15:09:38 +01:00
|
|
|
(all being a power of `10`) that can be prepended to all named units to obtain various scaled
|
|
|
|
|
versions of them.
|
|
|
|
|
|
|
|
|
|
Implementation of `std::ratio` provided by all major compilers is able to express only
|
|
|
|
|
16 of them. This is why, in the **mp-units**, we had to find an alternative way to represent
|
|
|
|
|
unit magnitude in a more flexible way.
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
Each prefix is implemented similarly to the following:
|
2023-06-29 15:09:38 +01:00
|
|
|
|
|
|
|
|
```cpp
|
2024-06-01 09:13:02 +02:00
|
|
|
template<PrefixableUnit U> struct quecto_ : prefixed_unit<"q", mag_power<10, -30>, U{}> {};
|
2024-09-05 08:43:36 +02:00
|
|
|
template<PrefixableUnit auto U> constexpr quecto_<decltype(U)> quecto;
|
2023-06-29 15:09:38 +01:00
|
|
|
```
|
|
|
|
|
|
2023-10-31 09:45:42 +01:00
|
|
|
and then a [PrefixableUnit](concepts.md#PrefixableUnit) can be prefixed in the following
|
2023-06-29 15:09:38 +01:00
|
|
|
way:
|
|
|
|
|
|
|
|
|
|
```cpp
|
2024-09-05 10:06:43 +02:00
|
|
|
inline constexpr auto qm = quecto<metre>;
|
2023-06-29 15:09:38 +01:00
|
|
|
```
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
The usage of `mag_power` not only enables providing support for SI prefixes, but it can also
|
2023-06-29 15:09:38 +01:00
|
|
|
efficiently represent any rational magnitude. For example, IEC 80000 prefixes used in the
|
|
|
|
|
IT industry can be implemented as:
|
|
|
|
|
|
|
|
|
|
```cpp
|
2024-06-01 09:13:02 +02:00
|
|
|
template<PrefixableUnit U> struct yobi_ : prefixed_unit<"Yi", mag_power<2, 80>, U{}> {};
|
2024-09-05 08:43:36 +02:00
|
|
|
template<PrefixableUnit auto U> constexpr yobi_<decltype(U)> yobi;
|
2023-06-29 15:09:38 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Scaled units
|
|
|
|
|
|
2023-08-03 21:23:34 +02:00
|
|
|
In the [SI](../../appendix/glossary.md#si), all units are either base or derived units or prefixed
|
2023-11-06 21:55:44 -10:00
|
|
|
versions of those. However, those are only some of the options possible.
|
2023-06-29 15:09:38 +01:00
|
|
|
|
2023-08-03 21:23:34 +02:00
|
|
|
For example, there is a list of [off-system units](../../appendix/glossary.md#off-system-unit)
|
2023-11-06 21:55:44 -10:00
|
|
|
accepted for use with SI. Those are scaled versions of the SI units with ratios that can't
|
2023-06-29 15:09:38 +01:00
|
|
|
be explicitly expressed with predefined SI prefixes. Those include units like minute, hour, or
|
|
|
|
|
electronvolt:
|
|
|
|
|
|
|
|
|
|
```cpp
|
2024-09-05 10:06:43 +02:00
|
|
|
inline constexpr struct minute final : named_unit<"min", mag<60> * si::second> {} minute;
|
|
|
|
|
inline constexpr struct hour final : named_unit<"h", mag<60> * minute> {} hour;
|
|
|
|
|
inline constexpr struct electronvolt final : named_unit<"eV", mag_ratio<1'602'176'634, 1'000'000'000> * mag_power<10, -19> * si::joule> {} electronvolt;
|
2023-06-29 15:09:38 +01:00
|
|
|
```
|
|
|
|
|
|
2023-08-03 21:23:34 +02:00
|
|
|
Also, units of other [systems of units](../../appendix/glossary.md#system-of-units) are often defined
|
2023-06-29 15:09:38 +01:00
|
|
|
in terms of scaled versions of the SI units. For example, the international yard is defined as:
|
|
|
|
|
|
|
|
|
|
```cpp
|
2024-09-05 10:06:43 +02:00
|
|
|
inline constexpr struct yard final : named_unit<"yd", mag_ratio<9'144, 10'000> * si::metre> {} yard;
|
2023-06-29 15:09:38 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For some units, a magnitude might also be irrational. The best example here is a `degree` which
|
|
|
|
|
is defined using a floating-point magnitude having a factor of the number π (Pi):
|
|
|
|
|
|
|
|
|
|
```cpp
|
2024-10-02 19:05:45 +02:00
|
|
|
inline constexpr struct pi final : mag_constant<symbol_text{u8"𝜋", "pi"}, std::numbers::pi_v<long double>> {} pi;
|
2024-10-14 22:49:58 +02:00
|
|
|
inline constexpr auto 𝜋 = pi;
|
2023-06-29 15:09:38 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```cpp
|
2024-10-14 22:49:58 +02:00
|
|
|
inline constexpr struct degree final : named_unit<{u8"°", "deg"}, mag<𝜋> / mag<180> * si::radian> {} degree;
|
2023-06-29 15:09:38 +01:00
|
|
|
```
|
2024-09-13 17:07:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Unit symbols
|
|
|
|
|
|
|
|
|
|
Units are available via their full names or through their short symbols.
|
|
|
|
|
To use a long version, it is enough to type:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
quantity q1 = 42 * si::metre / si::second;
|
|
|
|
|
quantity q2 = 42 * si::kilo<si::metre> / si::hour;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
To simplify how we spell it a short, user-friendly symbols are provided in a dedicated
|
|
|
|
|
subnamespace in systems definitions:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
namespace si::unit_symbols {
|
|
|
|
|
|
|
|
|
|
constexpr auto m = si::metre;
|
|
|
|
|
constexpr auto km = si::kilo<si::metre>;
|
|
|
|
|
constexpr auto s = si::second;
|
|
|
|
|
constexpr auto h = si::hour;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Unit symbols introduce a lot of short identifiers into the current namespace. This is why they
|
|
|
|
|
are opt-in. A user has to explicitly "import" them from a dedicated `unit_symbols` namespace:
|
|
|
|
|
|
|
|
|
|
=== "using-declaration"
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
using namespace si::unit_symbols;
|
|
|
|
|
|
|
|
|
|
quantity q1 = 42 * m / s;
|
|
|
|
|
quantity q2 = 42 * km / h;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
=== "using-directive"
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
using si::unit_symbols::m;
|
|
|
|
|
using si::unit_symbols::km;
|
|
|
|
|
using si::unit_symbols::s;
|
|
|
|
|
using si::unit_symbols::h;
|
|
|
|
|
|
|
|
|
|
quantity q1 = 42 * m / s;
|
|
|
|
|
quantity q2 = 42 * km / h;
|
|
|
|
|
```
|
|
|
|
|
|
2024-10-10 00:02:08 +02:00
|
|
|
We also provide alternative object identifiers using UTF-8 characters in their names for most
|
|
|
|
|
unit symbols. The code using UTF-8 looks nicer, but it is harder to type on the keyboard.
|
2024-09-13 17:07:03 +02:00
|
|
|
This is why we provide both versions of identifiers for such units.
|
|
|
|
|
|
2024-10-10 00:02:08 +02:00
|
|
|
=== "Portable"
|
2024-09-13 17:07:03 +02:00
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
quantity resistance = 60 * kohm;
|
|
|
|
|
quantity capacitance = 100 * uF;
|
|
|
|
|
```
|
|
|
|
|
|
2024-10-10 00:02:08 +02:00
|
|
|
=== "With UTF-8 glyphs"
|
2024-09-13 17:07:03 +02:00
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
quantity resistance = 60 * kΩ;
|
|
|
|
|
quantity capacitance = 100 * µF;
|
|
|
|
|
```
|
2024-09-26 20:28:41 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Common units
|
|
|
|
|
|
2024-10-15 20:52:51 +02:00
|
|
|
Adding, subtracting, or comparing two quantities of different units will force the library to find
|
|
|
|
|
a common unit for those. This is to prevent data truncation. For the cases when one of the units is
|
|
|
|
|
an integral multiple of the another, the resulting quantity will use a "smaller" one in its result.
|
2024-09-26 20:28:41 +02:00
|
|
|
For example:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
static_assert((1 * kg + 1 * g).unit == g);
|
|
|
|
|
static_assert((1 * km + 1 * mm).unit == mm);
|
|
|
|
|
static_assert((1 * yd + 1 * mi).unit == yd);
|
|
|
|
|
```
|
|
|
|
|
|
2024-10-15 20:52:51 +02:00
|
|
|
However, in many cases an arithmetic operation on quantities of different units will result in
|
|
|
|
|
a yet another unit. This happens when none of the source units is an integral multiple of another.
|
|
|
|
|
In such cases, the library returns a special type that denotes that we are dealing with a common
|
|
|
|
|
unit of such an equation:
|
2024-09-26 20:28:41 +02:00
|
|
|
|
|
|
|
|
```cpp
|
2024-10-05 18:11:18 +02:00
|
|
|
quantity q1 = 1 * km + 1 * mi; // quantity<common_unit<international::mile, si::kilo_<si::metre>>{}, int>
|
2024-10-15 20:52:51 +02:00
|
|
|
quantity q2 = 1. * rad + 1. * deg; // quantity<common_unit<si::degree, si::radian>{}, double>
|
2024-09-26 20:28:41 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
!!! note
|
|
|
|
|
|
|
|
|
|
A user should never explicitly instantiate a `common_unit` class template. The library's
|
|
|
|
|
framework will do it based on the provided quantity equation.
|