2023-06-23 19:15:07 +02:00
|
|
|
|
# Systems of Quantities
|
|
|
|
|
|
|
|
|
|
The physical units libraries on the market typically only scope on modeling one or more
|
2023-08-03 21:23:34 +02:00
|
|
|
|
[systems of units](../../appendix/glossary.md#system-of-units). However, this is not the
|
2023-06-23 19:15:07 +02:00
|
|
|
|
only system kind to model. Another, and maybe even more important, system kind is a
|
2023-08-03 21:23:34 +02:00
|
|
|
|
[system of quantities](../../appendix/glossary.md#system-of-quantities).
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
!!! info
|
|
|
|
|
|
|
|
|
|
Please note that the **mp-units** is probably the first library on the Open Source market
|
2023-08-03 21:23:34 +02:00
|
|
|
|
(in any programming language) that models the [ISQ](../../appendix/glossary.md#isq)
|
2023-06-23 19:15:07 +02:00
|
|
|
|
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:
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
Another typical question many users ask is how to deal with _work_ and _torque_.
|
2023-06-23 19:15:07 +02:00
|
|
|
|
Both of those have the same dimension but are different quantities.
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
A similar issue is related to figuring out what should be the result of:
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
auto res = 1 * Hz + 1 * Bq + 1 * Bd;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
where:
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
- `Hz` (hertz) - unit of _frequency_
|
|
|
|
|
- `Bq` (becquerel) - unit of _activity_
|
|
|
|
|
- `Bd` (baud) - unit of _modulation rate_
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
If the above example seems too abstract, let's consider a _fuel consumption_ (fuel _volume_
|
|
|
|
|
divided by _distance_, e.g., `6.7 l/km`) and an _area_. Again, both have the same dimension
|
|
|
|
|
$\mathsf{L}^{2}$, but probably it wouldn't be wise to allow adding, subtracting, or comparing
|
|
|
|
|
a _fuel consumption_ of a car and the _area_ of a football field. Such an operation does not
|
|
|
|
|
have any physical sense and should fail to compile.
|
|
|
|
|
|
2023-08-30 11:33:30 +02:00
|
|
|
|
!!! important
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
More than one quantity may be defined for the same dimension:
|
2023-08-03 21:23:34 +02:00
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
- quantities of **different kinds** (e.g. _frequency_, _modulation rate_, _activity_, ...)
|
|
|
|
|
- quantities of **the same kind** (e.g. _length_, _width_, _altitude_, _distance_, _radius_,
|
|
|
|
|
_wavelength_, _position vector_, ...)
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
It turns out that the above issues can't be solved correctly without proper modeling of
|
2023-08-03 21:23:34 +02:00
|
|
|
|
a [system of quantities](../../appendix/glossary.md#system-of-quantities).
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 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**
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
The above quotes from ISO 80000 provide answers to all the issues above. Two quantities can't be
|
2023-08-03 21:23:34 +02:00
|
|
|
|
added, subtracted, or compared unless they belong to the same [kind](../../appendix/glossary.md#kind).
|
2024-09-30 18:25:53 +02:00
|
|
|
|
As _frequency_, _activity_, and _modulation rate_ are of different kinds, the expression provided
|
|
|
|
|
above should not compile.
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 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
|
2024-09-28 15:54:42 +02:00
|
|
|
|
length["<b>length</b><br>[m]"]
|
|
|
|
|
length --- width["<b>width</b> / <b>breadth</b>"]
|
|
|
|
|
length --- height["<b>height</b> / <b>depth</b> / <b>altitude</b>"]
|
|
|
|
|
width --- thickness["<b>thickness</b>"]
|
|
|
|
|
width --- diameter["<b>diameter</b>"]
|
|
|
|
|
width --- radius["<b>radius</b>"]
|
|
|
|
|
length --- path_length["<b>path_length</b>"]
|
|
|
|
|
path_length --- distance["<b>distance</b>"]
|
|
|
|
|
distance --- radial_distance["<b>radial_distance</b>"]
|
|
|
|
|
length --- wavelength["<b>wavelength</b>"]
|
|
|
|
|
length --- displacement["<b>displacement</b><br>{vector}"]
|
2024-11-06 18:30:51 +01:00
|
|
|
|
displacement --- position_vector["<b>position_vector</b>"]
|
2024-09-28 15:54:42 +02:00
|
|
|
|
radius --- radius_of_curvature["<b>radius_of_curvature</b>"]
|
2023-06-23 19:15:07 +02:00
|
|
|
|
```
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
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 requires a different
|
2023-06-23 19:15:07 +02:00
|
|
|
|
representation type (notice that `position_vector` and `displacement` are vector quantities).
|
|
|
|
|
|
2024-09-30 18:25:53 +02:00
|
|
|
|
Forming such a hierarchy helps us in defining arithmetics and conversion rules for various
|
|
|
|
|
quantities of the same kind.
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
|
2023-07-07 18:22:16 +02:00
|
|
|
|
## Defining quantities
|
|
|
|
|
|
|
|
|
|
In the **mp-units** library all the information about the quantity is provided with the `quantity_spec`
|
|
|
|
|
class template. In order to define a specific quantity a user should inherit a strong type
|
|
|
|
|
from such an instantiation.
|
|
|
|
|
|
2023-08-26 19:47:10 +02:00
|
|
|
|
!!! tip
|
|
|
|
|
|
|
|
|
|
Quantity specification definitions benefit from an
|
|
|
|
|
[explicit object parameter](https://en.cppreference.com/w/cpp/language/member_functions#Explicit_object_parameter)
|
|
|
|
|
added in C++23 to remove the need for CRTP idiom, which significantly simplifies the code.
|
2024-04-16 21:48:36 +01:00
|
|
|
|
However, as C++23 is far from being mainstream today,
|
|
|
|
|
a [portability macro `QUANTITY_SPEC()`](../use_cases/wide_compatibility.md#QUANTITY_SPEC)
|
2023-08-26 19:47:10 +02:00
|
|
|
|
is provided and used consistently through the library to allow the code to compile with C++20
|
|
|
|
|
compilers, thanks to the CRTP usage under the hood.
|
|
|
|
|
|
2024-04-16 21:48:36 +01:00
|
|
|
|
See more in the
|
|
|
|
|
[C++ compiler support](../../getting_started/cpp_compiler_support.md#explicit-this-parameter)
|
|
|
|
|
chapter.
|
|
|
|
|
|
2024-02-22 11:23:22 +01:00
|
|
|
|
*[CRTP]: Curiously Recurring Template Parameter
|
2023-08-26 19:47:10 +02:00
|
|
|
|
|
2023-07-07 18:22:16 +02:00
|
|
|
|
For example, here is how the above quantity kind tree can be modeled in the library:
|
|
|
|
|
|
2023-07-10 12:33:54 +02:00
|
|
|
|
=== "C++23"
|
|
|
|
|
|
|
|
|
|
```cpp
|
2024-09-05 10:06:43 +02:00
|
|
|
|
inline constexpr struct length final : quantity_spec<dim_length> {} length;
|
|
|
|
|
inline constexpr struct width final : quantity_spec<length> {} width;
|
|
|
|
|
inline constexpr auto breadth = width;
|
|
|
|
|
inline constexpr struct height final : quantity_spec<length> {} height;
|
|
|
|
|
inline constexpr auto depth = height;
|
|
|
|
|
inline constexpr auto altitude = height;
|
|
|
|
|
inline constexpr struct thickness final : quantity_spec<width> {} thickness;
|
|
|
|
|
inline constexpr struct diameter final : quantity_spec<width> {} diameter;
|
|
|
|
|
inline constexpr struct radius final : quantity_spec<width> {} radius;
|
|
|
|
|
inline constexpr struct radius_of_curvature final : quantity_spec<radius> {} radius_of_curvature;
|
|
|
|
|
inline constexpr struct path_length final : quantity_spec<length> {} path_length;
|
|
|
|
|
inline constexpr auto arc_length = path_length;
|
|
|
|
|
inline constexpr struct distance final : quantity_spec<path_length> {} distance;
|
|
|
|
|
inline constexpr struct radial_distance final : quantity_spec<distance> {} radial_distance;
|
|
|
|
|
inline constexpr struct wavelength final : quantity_spec<length> {} wavelength;
|
|
|
|
|
inline constexpr struct displacement final : quantity_spec<length, quantity_character::vector> {} displacement;
|
2024-11-06 18:30:51 +01:00
|
|
|
|
inline constexpr struct position_vector final : quantity_spec<displacement> {} position_vector;
|
2023-07-10 12:33:54 +02:00
|
|
|
|
```
|
|
|
|
|
|
2023-07-11 17:22:32 +02:00
|
|
|
|
=== "C++20"
|
|
|
|
|
|
|
|
|
|
```cpp
|
2024-09-05 10:06:43 +02:00
|
|
|
|
inline constexpr struct length final : quantity_spec<length, dim_length> {} length;
|
|
|
|
|
inline constexpr struct width final : quantity_spec<width, length> {} width;
|
|
|
|
|
inline constexpr auto breadth = width;
|
|
|
|
|
inline constexpr struct height final : quantity_spec<height, length> {} height;
|
|
|
|
|
inline constexpr auto depth = height;
|
|
|
|
|
inline constexpr auto altitude = height;
|
|
|
|
|
inline constexpr struct thickness final : quantity_spec<thickness, width> {} thickness;
|
|
|
|
|
inline constexpr struct diameter final : quantity_spec<diameter, width> {} diameter;
|
|
|
|
|
inline constexpr struct radius final : quantity_spec<radius, width> {} radius;
|
|
|
|
|
inline constexpr struct radius_of_curvature final : quantity_spec<radius_of_curvature, radius> {} radius_of_curvature;
|
|
|
|
|
inline constexpr struct path_length final : quantity_spec<path_length, length> {} path_length;
|
|
|
|
|
inline constexpr auto arc_length = path_length;
|
|
|
|
|
inline constexpr struct distance final : quantity_spec<distance, path_length> {} distance;
|
|
|
|
|
inline constexpr struct radial_distance final : quantity_spec<radial_distance, distance> {} radial_distance;
|
|
|
|
|
inline constexpr struct wavelength final : quantity_spec<wavelength, length> {} wavelength;
|
|
|
|
|
inline constexpr struct displacement final : quantity_spec<displacement, length, quantity_character::vector> {} displacement;
|
2024-11-06 18:30:51 +01:00
|
|
|
|
inline constexpr struct position_vector final : quantity_spec<position_vector, displacement> {} position_vector;
|
2023-07-11 17:22:32 +02:00
|
|
|
|
```
|
|
|
|
|
|
2023-07-10 12:33:54 +02:00
|
|
|
|
=== "Portable"
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
QUANTITY_SPEC(length, dim_length);
|
|
|
|
|
QUANTITY_SPEC(width, length);
|
2024-09-05 10:06:43 +02:00
|
|
|
|
inline constexpr auto breadth = width;
|
2023-07-10 12:33:54 +02:00
|
|
|
|
QUANTITY_SPEC(height, length);
|
2024-09-05 10:06:43 +02:00
|
|
|
|
inline constexpr auto depth = height;
|
|
|
|
|
inline constexpr auto altitude = height;
|
2023-07-10 12:33:54 +02:00
|
|
|
|
QUANTITY_SPEC(thickness, width);
|
|
|
|
|
QUANTITY_SPEC(diameter, width);
|
|
|
|
|
QUANTITY_SPEC(radius, width);
|
|
|
|
|
QUANTITY_SPEC(radius_of_curvature, radius);
|
|
|
|
|
QUANTITY_SPEC(path_length, length);
|
2024-09-05 10:06:43 +02:00
|
|
|
|
inline constexpr auto arc_length = path_length;
|
2023-07-10 12:33:54 +02:00
|
|
|
|
QUANTITY_SPEC(distance, path_length);
|
|
|
|
|
QUANTITY_SPEC(radial_distance, distance);
|
|
|
|
|
QUANTITY_SPEC(wavelength, length);
|
|
|
|
|
QUANTITY_SPEC(displacement, length, quantity_character::vector);
|
2024-11-06 18:30:51 +01:00
|
|
|
|
QUANTITY_SPEC(position_vector, displacement);
|
2023-07-10 12:33:54 +02:00
|
|
|
|
```
|
2023-07-07 18:22:16 +02:00
|
|
|
|
|
|
|
|
|
!!! note
|
|
|
|
|
|
|
|
|
|
More information on how to define a system of quantities can be found in the
|
2024-06-25 13:49:50 -05:00
|
|
|
|
["International System of Quantities (ISQ)"](../systems/isq.md) chapter.
|
2023-07-07 18:22:16 +02:00
|
|
|
|
|
|
|
|
|
|
2023-06-23 19:15:07 +02:00
|
|
|
|
## Comparing, adding, and subtracting quantities
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
ISO 80000 explicitly states that _width_ and _height_ are quantities of the same kind, and as such they:
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
- are mutually comparable,
|
|
|
|
|
- can be added and subtracted.
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
If we take the above for granted, the only reasonable result of `1 * width + 1 * height` is `2 * length`,
|
2023-11-06 21:55:44 -10:00
|
|
|
|
where the result of `length` is known as a **common quantity** type. A result of such an equation is always
|
|
|
|
|
the first common node in a hierarchy tree of the same kind. For example:
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
```cpp
|
2024-09-24 09:36:59 +02:00
|
|
|
|
static_assert(get_common_quantity_spec(isq::width, isq::height) == isq::length);
|
|
|
|
|
static_assert(get_common_quantity_spec(isq::thickness, isq::radius) == isq::width);
|
|
|
|
|
static_assert(get_common_quantity_spec(isq::distance, isq::path_length) == isq::path_length);
|
2023-06-23 19:15:07 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Converting between quantities
|
|
|
|
|
|
|
|
|
|
Based on the same hierarchy of quantities of kind length, we can define quantity conversion rules.
|
|
|
|
|
|
|
|
|
|
1. **Implicit conversions**
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
- every _width_ is a _length_
|
|
|
|
|
- every _radius_ is a _width_
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
static_assert(implicitly_convertible(isq::width, isq::length));
|
|
|
|
|
static_assert(implicitly_convertible(isq::radius, isq::width));
|
2023-11-06 21:55:44 -10:00
|
|
|
|
static_assert(implicitly_convertible(isq::radius, isq::length));
|
2023-06-23 19:15:07 +02:00
|
|
|
|
```
|
|
|
|
|
|
2025-06-20 17:20:57 +02:00
|
|
|
|
Implicit conversions are allowed on copy-initialization:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
void foo(quantity<isq::length[m]> q);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
quantity<isq::width[m]> q1 = 42 * m;
|
|
|
|
|
quantity<isq::length[m]> q2 = q1; // implicit quantity conversion
|
|
|
|
|
foo(q1); // implicit quantity conversion
|
|
|
|
|
```
|
|
|
|
|
|
2023-06-23 19:15:07 +02:00
|
|
|
|
2. **Explicit conversions**
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
- not every _length_ is a _width_
|
|
|
|
|
- not every _width_ is a _radius_
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
static_assert(!implicitly_convertible(isq::length, isq::width));
|
|
|
|
|
static_assert(!implicitly_convertible(isq::width, isq::radius));
|
2023-11-06 21:55:44 -10:00
|
|
|
|
static_assert(!implicitly_convertible(isq::length, isq::radius));
|
2023-06-23 19:15:07 +02:00
|
|
|
|
static_assert(explicitly_convertible(isq::length, isq::width));
|
|
|
|
|
static_assert(explicitly_convertible(isq::width, isq::radius));
|
2023-11-06 21:55:44 -10:00
|
|
|
|
static_assert(explicitly_convertible(isq::length, isq::radius));
|
2023-06-23 19:15:07 +02:00
|
|
|
|
```
|
|
|
|
|
|
2025-06-20 17:20:57 +02:00
|
|
|
|
Explicit conversions are forced by passing the quantity to a call operator of a `quantity_spec`
|
|
|
|
|
type or by calling `quantity`'s explicit constructor:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
void foo(quantity<isq::height[m]> q);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
quantity<isq::length[m]> q1 = 42 * m;
|
|
|
|
|
quantity<isq::height[m]> q2 = isq::height(q1); // explicit quantity conversion
|
|
|
|
|
quantity<isq::height[m]> q3(q1); // direct initialization
|
|
|
|
|
foo(isq::height(q1)); // explicit quantity conversion
|
|
|
|
|
```
|
|
|
|
|
|
2023-06-23 19:15:07 +02:00
|
|
|
|
3. **Explicit casts**
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
- _height_ is not a _width_
|
|
|
|
|
- both _height_ and _width_ are quantities of kind _length_
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
static_assert(!implicitly_convertible(isq::height, isq::width));
|
|
|
|
|
static_assert(!explicitly_convertible(isq::height, isq::width));
|
|
|
|
|
static_assert(castable(isq::height, isq::width));
|
|
|
|
|
```
|
|
|
|
|
|
2025-06-20 17:20:57 +02:00
|
|
|
|
Explicit casts are forced with a dedicated `quantity_cast` function:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
void foo(quantity<isq::height[m]> q);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
quantity<isq::width[m]> q1 = 42 * m;
|
|
|
|
|
quantity<isq::height[m]> q2 = quantity_cast<isq::height>(q1); // explicit quantity cast
|
|
|
|
|
foo(quantity_cast<isq::height>(q1)); // explicit quantity cast
|
|
|
|
|
```
|
|
|
|
|
|
2023-06-23 19:15:07 +02:00
|
|
|
|
4. **No conversion**
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
- _time_ has nothing in common with _length_
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
static_assert(!implicitly_convertible(isq::time, isq::length));
|
|
|
|
|
static_assert(!explicitly_convertible(isq::time, isq::length));
|
|
|
|
|
static_assert(!castable(isq::time, isq::length));
|
|
|
|
|
```
|
|
|
|
|
|
2025-06-20 17:20:57 +02:00
|
|
|
|
Even the explicit casts will not force such a conversion:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
void foo(quantity<isq::length[m]>);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
quantity<isq::length[m]> q1 = 42 * s; // Compile-time error
|
|
|
|
|
foo(quantity_cast<isq::length>(42 * s)); // Compile-time error
|
|
|
|
|
```
|
|
|
|
|
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
## 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.
|
|
|
|
|
|
2024-10-01 19:52:06 +02:00
|
|
|
|
The below presents some arbitrary hierarchy of derived quantities of kind _energy_:
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
flowchart TD
|
2024-09-28 15:54:42 +02:00
|
|
|
|
energy["<b>energy</b><br><i>(mass * length<sup>2</sup> / time<sup>2</sup>)</i><br>[J]"]
|
|
|
|
|
energy --- mechanical_energy["<b>mechanical_energy</b>"]
|
|
|
|
|
mechanical_energy --- potential_energy["<b>potential_energy</b>"]
|
|
|
|
|
potential_energy --- gravitational_potential_energy["<b>gravitational_potential_energy</b><br><i>(mass * acceleration_of_free_fall * height)</i>"]
|
|
|
|
|
potential_energy --- elastic_potential_energy["<b>elastic_potential_energy</b><br><i>(spring_constant * amount_of_compression<sup>2</sup>)</i>"]
|
|
|
|
|
mechanical_energy --- kinetic_energy["<b>kinetic_energy</b><br><i>(mass * speed<sup>2</sup>)</i>"]
|
|
|
|
|
energy --- enthalpy["<b>enthalpy</b>"]
|
|
|
|
|
enthalpy --- internal_energy["<b>internal_energy</b> / <b>thermodynamic_energy</b>"]
|
|
|
|
|
internal_energy --- Helmholtz_energy["<b>Helmholtz_energy</b> / <b>Helmholtz_function</b>"]
|
|
|
|
|
enthalpy --- Gibbs_energy["<b>Gibbs_energy</b> / <b>Gibbs_function</b>"]
|
|
|
|
|
energy --- active_energy["<b>active_energy</b>"]
|
2023-06-23 19:15:07 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Notice, that even though all of those quantities have the same dimension and can be expressed
|
2023-08-03 21:23:34 +02:00
|
|
|
|
in the same units, they have different [quantity equations](../../appendix/glossary.md#quantity-equation)
|
2023-11-06 21:55:44 -10:00
|
|
|
|
that can be used to create them implicitly:
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
- _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 from their
|
|
|
|
|
trees 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_:
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
```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));
|
|
|
|
|
```
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
- _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.md#quantity-equation):
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
```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));
|
|
|
|
|
```
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
- _gravitational potential energy_ is not only even more specialized one but additionally,
|
2023-06-23 19:15:07 +02:00
|
|
|
|
it is special in a way that it provides its own "constrained"
|
2023-08-03 21:23:34 +02:00
|
|
|
|
[quantity equation](../../appendix/glossary.md#quantity-equation). Maybe not every
|
2023-11-06 21:55:44 -10:00
|
|
|
|
`mass * pow<2>(length) / pow<2>(time)` is a _gravitational potential energy_, but every
|
2023-06-23 19:15:07 +02:00
|
|
|
|
`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)
|
2024-10-01 19:52:06 +02:00
|
|
|
|
we introduced a `kind_of<>` specifier. For example, to express any quantity of _length_, we need
|
2023-06-23 19:15:07 +02:00
|
|
|
|
to type `kind_of<isq::length>`.
|
|
|
|
|
|
2023-08-30 11:33:30 +02:00
|
|
|
|
!!! important
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
`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>>);
|
|
|
|
|
```
|
|
|
|
|
|
2023-11-06 21:55:44 -10:00
|
|
|
|
However, if at least one equation's operand is not a quantity kind, the result becomes a "strong"
|
2023-06-23 19:15:07 +02:00
|
|
|
|
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>);
|
|
|
|
|
```
|
|
|
|
|
|
2023-08-30 11:33:30 +02:00
|
|
|
|
!!! info
|
2023-06-23 19:15:07 +02:00
|
|
|
|
|
|
|
|
|
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`
|
2024-02-13 20:25:28 +01:00
|
|
|
|
specifier. For example, `kind_of<isq::width>` will fail to compile. However, we can call
|
|
|
|
|
`get_kind(q)` to obtain a kind of any quantity:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
static_assert(get_kind(isq::width) == kind_of<isq::length>);
|
|
|
|
|
```
|