mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-03 20:34:26 +02:00
docs: "Systems of Quantities" chapter added
This commit is contained in:
@@ -0,0 +1,296 @@
|
|||||||
|
# Systems of Quantities
|
||||||
|
|
||||||
|
The physical units libraries on the market typically only scope on modeling one or more
|
||||||
|
[systems of units](../../../appendix/glossary/#system-of-units). However, this is not the
|
||||||
|
only system kind to model. Another, and maybe even more important, system kind is a
|
||||||
|
[system of quantities](../../../appendix/glossary/#system-of-quantities).
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
|
||||||
|
Please note that the **mp-units** is probably the first library on the Open Source market
|
||||||
|
(in any programming language) that models the [ISQ](../../../appendix/glossary/#isq)
|
||||||
|
with all its definitions provided in ISO 80000. Please provide feedback if something
|
||||||
|
looks odd or could be improved.
|
||||||
|
|
||||||
|
|
||||||
|
## Dimension is not enough to describe a quantity
|
||||||
|
|
||||||
|
Most of the products on the market are aware of physical dimensions. However, a dimension is not
|
||||||
|
enough to describe a quantity. For example, let's see the following implementation:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Box {
|
||||||
|
area base_;
|
||||||
|
length height_;
|
||||||
|
public:
|
||||||
|
Box(length l, length w, length h) : base_(l * w), height_(h) {}
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
Box my_box(2 * m, 3 * m, 1 * m);
|
||||||
|
```
|
||||||
|
|
||||||
|
How do you like such an interface? It turns out that in most existing strongly-typed libraries
|
||||||
|
this is often the best we can do :woozy_face:
|
||||||
|
|
||||||
|
Another typical question many users ask is how to deal with energy and moment of force.
|
||||||
|
Both of those have the same dimension but are different quantities.
|
||||||
|
|
||||||
|
Another question is what should be the result of:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto res = 1 * Hz + 1 * Bq + 1 * Bd;
|
||||||
|
```
|
||||||
|
|
||||||
|
where:
|
||||||
|
|
||||||
|
- `Hz` (hertz) - unit of frequency
|
||||||
|
- `Bq` (becquerel) - unit of activity
|
||||||
|
- `Bd` (baud) - unit of modulation rate
|
||||||
|
|
||||||
|
All of those quantities have the same dimension, namely $\mathsf{T}^{-1}$, but probably it
|
||||||
|
is not wise to allow adding, subtracting, or comparing them, as they describe vastly different
|
||||||
|
physical properties.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
|
||||||
|
More than one quantity may be defined for the same dimension:
|
||||||
|
|
||||||
|
- quantities of _different kinds_ (i.e. frequency, modulation rate, activity, ...)
|
||||||
|
- quantities of _the same kind_ (i.e. length, width, altitude, distance, radius, wavelength, position vector, ...)
|
||||||
|
|
||||||
|
It turns out that the above issues can't be solved correctly without proper modeling of
|
||||||
|
a [system of quantities](../../../appendix/glossary/#system-of-quantities).
|
||||||
|
|
||||||
|
|
||||||
|
## Quantities of the same kind
|
||||||
|
|
||||||
|
!!! quote "ISO 80000-1"
|
||||||
|
|
||||||
|
- Quantities may be grouped together into categories of quantities that are
|
||||||
|
**mutually comparable**
|
||||||
|
- Mutually comparable quantities are called **quantities of the same kind**
|
||||||
|
- Two or more quantities **cannot be added or subtracted unless they belong to the same category
|
||||||
|
of mutually comparable quantities**
|
||||||
|
- Quantities of the **same kind** within a given system of quantities **have the same quantity
|
||||||
|
dimension**
|
||||||
|
- Quantities of the **same dimension are not necessarily of the same kind**
|
||||||
|
|
||||||
|
The above quotes from ISO 80000 answer to all the issues above. Two quantities can't be
|
||||||
|
added, subtracted, or compared unless they belong to the same [kind](../../../appendix/glossary/#kind).
|
||||||
|
As frequency, activity, and modulation rate are different kinds, the expression provided above should
|
||||||
|
not compile.
|
||||||
|
|
||||||
|
|
||||||
|
## System of quantities is not only about kinds
|
||||||
|
|
||||||
|
ISO 80000 specify hundreds of different quantities. There are plenty of different kinds provided
|
||||||
|
and often each kind contains more than one quantity. In fact, it turns out that such quantities
|
||||||
|
form a hierarchy of quantities of the same kind.
|
||||||
|
|
||||||
|
For example, here are all quantities of the kind length provided in the ISO 80000:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
length --- width[width, breadth]
|
||||||
|
length --- height[height, depth, altitude]
|
||||||
|
width --- thickness
|
||||||
|
width --- diameter
|
||||||
|
width --- radius
|
||||||
|
length --- path_length
|
||||||
|
path_length --- distance
|
||||||
|
distance --- radial_distance
|
||||||
|
length --- wavelength
|
||||||
|
length --- position_vector["position_vector\n{vector}"]
|
||||||
|
length --- displacement["displacement\n{vector}"]
|
||||||
|
radius --- radius_of_curvature
|
||||||
|
```
|
||||||
|
|
||||||
|
Each of the above quantities expresses some kind of length, and each can be measured with `si::metre`.
|
||||||
|
However, each of them has different properties, usage, and sometimes even a different
|
||||||
|
representation type (notice that `position_vector` and `displacement` are vector quantities).
|
||||||
|
|
||||||
|
Analyzing such a hierarchy can help us in defining arithmetics and conversion rules.
|
||||||
|
|
||||||
|
|
||||||
|
## Comparing, adding, and subtracting quantities
|
||||||
|
|
||||||
|
ISO 80000 explicitly states that `width` and `height` are quantities of the same kind, and as such they:
|
||||||
|
|
||||||
|
- are mutually comparable
|
||||||
|
- can be added and subtracted
|
||||||
|
|
||||||
|
If we take the above for granted, the only reasonable result of `1 * width + 1 * height` is `2 * length`,
|
||||||
|
where the result of `length` is known as a common quantity type. A result of such an equation is always
|
||||||
|
the first common branch in a hierarchy tree of the same kind. For example:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(common_quantity_spec(isq::width, isq::height) == isq::length);
|
||||||
|
static_assert(common_quantity_spec(isq::thickness, isq::radius) == isq::width);
|
||||||
|
static_assert(common_quantity_spec(isq::distance, isq::path_length) == isq::path_length);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Converting between quantities
|
||||||
|
|
||||||
|
Based on the same hierarchy of quantities of kind length, we can define quantity conversion rules.
|
||||||
|
|
||||||
|
1. **Implicit conversions**
|
||||||
|
|
||||||
|
- every `width` is a `length`
|
||||||
|
- every `radius` is a `width`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(implicitly_convertible(isq::width, isq::length));
|
||||||
|
static_assert(implicitly_convertible(isq::radius, isq::length));
|
||||||
|
static_assert(implicitly_convertible(isq::radius, isq::width));
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Explicit conversions**
|
||||||
|
|
||||||
|
- not every `length` is a `width`
|
||||||
|
- not every `width` is a `radius`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(!implicitly_convertible(isq::length, isq::width));
|
||||||
|
static_assert(!implicitly_convertible(isq::length, isq::radius));
|
||||||
|
static_assert(!implicitly_convertible(isq::width, isq::radius));
|
||||||
|
static_assert(explicitly_convertible(isq::length, isq::width));
|
||||||
|
static_assert(explicitly_convertible(isq::length, isq::radius));
|
||||||
|
static_assert(explicitly_convertible(isq::width, isq::radius));
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Explicit casts**
|
||||||
|
|
||||||
|
- `height` is not a `width`
|
||||||
|
- both `height` and `width` are quantities of kind `length`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(!implicitly_convertible(isq::height, isq::width));
|
||||||
|
static_assert(!explicitly_convertible(isq::height, isq::width));
|
||||||
|
static_assert(castable(isq::height, isq::width));
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **No conversion**
|
||||||
|
|
||||||
|
- `time` has nothing in common with `length`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(!implicitly_convertible(isq::time, isq::length));
|
||||||
|
static_assert(!explicitly_convertible(isq::time, isq::length));
|
||||||
|
static_assert(!castable(isq::time, isq::length));
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Hierarchies of derived quantities
|
||||||
|
|
||||||
|
Derived quantity equations often do not automatically form a hierarchy tree. This is why it is
|
||||||
|
sometimes not obvious what such a tree should look like. Also, ISO explicitly states:
|
||||||
|
|
||||||
|
!!! quote "ISO/IEC Guide 99"
|
||||||
|
|
||||||
|
The division of ‘quantity’ according to ‘kind of quantity’ is, to some extent, arbitrary.
|
||||||
|
|
||||||
|
The below presents some arbitrary hierarchy of derived quantities of kind energy:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
energy["energy\n(mass * length^2 / time^2)"]
|
||||||
|
energy --- mechanical_energy
|
||||||
|
mechanical_energy --- potential_energy
|
||||||
|
potential_energy --- gravitational_potential_energy["gravitational_potential_energy\n(mass / acceleration_of_free_fall / height)"]
|
||||||
|
potential_energy --- elastic_potential_energy["elastic_potential_energy\n(spring_constant * amount_of_compression^2)"]
|
||||||
|
mechanical_energy --- kinetic_energy["kinetic_energy\n(mass * speed^2)"]
|
||||||
|
energy --- enthalpy
|
||||||
|
enthalpy --- internal_energy[internal_energy, thermodynamic_energy]
|
||||||
|
internal_energy --- Helmholtz_energy[Helmholtz_energy, Helmholtz_function]
|
||||||
|
enthalpy --- Gibbs_energy[Gibbs_energy, Gibbs_function]
|
||||||
|
energy --- active_energy
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice, that even though all of those quantities have the same dimension and can be expressed
|
||||||
|
in the same units, they have different [quantity equations](../../../appendix/glossary/#quantity-equation)
|
||||||
|
used to create them implicitly:
|
||||||
|
|
||||||
|
- `energy` is the most generic one and thus can be created from base quantities of `mass`, `length`,
|
||||||
|
and `time`. As those are also the roots of quantities of their kinds and all other quantities are
|
||||||
|
implicitly convertible to them (we agreed on that "every `width` is a `length`" already), it means
|
||||||
|
that an `energy` can be implicitly constructed from any quantity of mass, length, and time.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time), isq::energy));
|
||||||
|
static_assert(implicitly_convertible(isq::mass * pow<2>(isq::height) / pow<2>(isq::time), isq::energy));
|
||||||
|
```
|
||||||
|
|
||||||
|
- `mechanical_energy` is a more "specialized" quantity than `energy` (not every `energy` is
|
||||||
|
a `mechanical_energy`). It is why an explicit cast is needed to convert from either `energy` or
|
||||||
|
the results of its [quantity equation](../../../appendix/glossary/#quantity-equation).
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(!implicitly_convertible(isq::energy, isq::mechanical_energy));
|
||||||
|
static_assert(explicitly_convertible(isq::energy, isq::mechanical_energy));
|
||||||
|
static_assert(!implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),
|
||||||
|
isq::mechanical_energy));
|
||||||
|
static_assert(explicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),
|
||||||
|
isq::mechanical_energy));
|
||||||
|
```
|
||||||
|
|
||||||
|
- `gravitational_potential_energy` is not only even more specialized one but additionally,
|
||||||
|
it is special in a way that it provides its own "constrained"
|
||||||
|
[quantity equation](../../../appendix/glossary/#quantity-equation). Maybe not every
|
||||||
|
`mass * pow<2>(length) / pow<2>(time)` is a `gravitational_potential_energy`, but every
|
||||||
|
`mass * acceleration_of_free_fall * height` is.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(!implicitly_convertible(isq::energy, gravitational_potential_energy));
|
||||||
|
static_assert(explicitly_convertible(isq::energy, gravitational_potential_energy));
|
||||||
|
static_assert(!implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),
|
||||||
|
gravitational_potential_energy));
|
||||||
|
static_assert(explicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),
|
||||||
|
gravitational_potential_energy));
|
||||||
|
static_assert(implicitly_convertible(isq::mass * isq::acceleration_of_free_fall * isq::height,
|
||||||
|
gravitational_potential_energy));
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Modeling a quantity kind
|
||||||
|
|
||||||
|
In the physical units library, we also need an abstraction describing an entire family of
|
||||||
|
quantities of the same kind. Such quantities have not only the same dimension but also
|
||||||
|
can be expressed in the same units.
|
||||||
|
|
||||||
|
To annotate a quantity to represent its kind (and not just a hierarchy tree's root quantity)
|
||||||
|
we introduced a `kind_of<>` specifier. For example, to express any quantity of length, we need
|
||||||
|
to type `kind_of<isq::length>`.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
`isq::length` and `kind_of<isq::length>` are two different things.
|
||||||
|
|
||||||
|
Such an entity behaves as any quantity of its kind. This means that it is implicitly
|
||||||
|
convertible to any quantity in a tree.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(!implicitly_convertible(isq::length, isq::height));
|
||||||
|
static_assert(implicitly_convertible(kind_of<isq::length>, isq::height));
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, the result of operations on quantity kinds is also a quantity kind:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(same_type<kind_of<isq::length> / kind_of<isq::time>, kind_of<isq::length / isq::time>>);
|
||||||
|
```
|
||||||
|
|
||||||
|
However, if at least one equation's operand is not a kind, the result becomes a "strong"
|
||||||
|
quantity where all the kinds are converted to the hierarchy tree's root quantities:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(!same_type<kind_of<isq::length> / isq::time, kind_of<isq::length / isq::time>>);
|
||||||
|
static_assert(same_type<kind_of<isq::length> / isq::time, isq::length / isq::time>);
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
Only a root quantity from the hierarchy tree or the one marked with `is_kind` specifier
|
||||||
|
in the `quantity_spec` definition can be put as a template parameter to the `kind_of`
|
||||||
|
specifier. For example, `kind_of<isq::width>` will fail to compile.
|
||||||
|
Reference in New Issue
Block a user