mirror of
https://github.com/mpusz/mp-units.git
synced 2025-07-29 18:07:16 +02:00
docs: 3 first articles of the ISQ series added
This commit is contained in:
145
docs/blog/posts/isq-part-1-introduction.md
Normal file
145
docs/blog/posts/isq-part-1-introduction.md
Normal file
@ -0,0 +1,145 @@
|
||||
---
|
||||
draft: true
|
||||
date: 2024-10-07
|
||||
authors:
|
||||
- mpusz
|
||||
categories:
|
||||
- Metrology
|
||||
comments: true
|
||||
---
|
||||
|
||||
# International System of Quantities (ISQ): Part 1 - Introduction
|
||||
|
||||
This post starts a series of articles about the International System of Quantities (ISQ).
|
||||
In this series, we will describe:
|
||||
|
||||
- What is ISQ?
|
||||
- Which engineering problems does ISQ help to solve and how?
|
||||
- What is missing in the ISQ, and why is that a problem?
|
||||
|
||||
<!-- more -->
|
||||
|
||||
## Terms and Definitions
|
||||
|
||||
From our experience, many people, including experts in the domain, often tend to name things
|
||||
differently, or sometimes they use the same term while having a different meaning in mind.
|
||||
This is why it is essential to stick to one well-defined glossary of terms
|
||||
for metrology.
|
||||
|
||||
The **mp-units** project consistently uses the official metrology vocabulary defined by the ISO
|
||||
and BIPM:
|
||||
|
||||
- [International Organization for Standardization (ISO)](https://www.iso.org/obp/ui#iso:std:iso-iec:guide:99:ed-1:v2:en),
|
||||
- [International Bureau of Weights and Measures (BIPM)](https://jcgm.bipm.org/vim/en).
|
||||
|
||||
The above are identical and contain the same set of definitions. We provide both to point out that
|
||||
the biggest institutions in standardizing metrology agree on the same vocabulary.
|
||||
|
||||
## Systems of Quantities vs Systems of Units
|
||||
|
||||
Here are the official definitions from our vocabulary:
|
||||
|
||||
!!! quote "[System of quantities](https://jcgm.bipm.org/vim/en/1.3.html)"
|
||||
|
||||
A **system of quantities** is a set of quantities together with a set of noncontradictory
|
||||
equations relating those quantities.
|
||||
|
||||
!!! quote "[System of units](https://jcgm.bipm.org/vim/en/1.13.html)"
|
||||
|
||||
A **system of units** is a set of base units and derived units, together with their multiples
|
||||
and submultiples, defined in accordance with given rules, for a given **system of quantities**.
|
||||
|
||||
From the definition above, we can find out that the systems of quantities and units form a hierarchy:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
system_of_quantities["System of Quantities"]
|
||||
system_of_quantities --- system_of_units1[System of Units #1]
|
||||
system_of_quantities --- system_of_units2[System of Units #2]
|
||||
system_of_quantities --- system_of_units3[System of Units #3]
|
||||
```
|
||||
|
||||
**System of quantities** defines quantities commonly used in engineering (e.g., _length_, _time_,
|
||||
_mass_, _speed_, _energy_, _power_, etc.) and relations between them. It does not assign any
|
||||
specific units to those quantities, though.
|
||||
|
||||
**Systems of units** are the ones that assign units of measurement to quantities from a specific
|
||||
**system of quantities** they chose to model. Different **systems of units** are free to chose
|
||||
whatever they find suitable for specific quantities and do not have to be consistent/compatible
|
||||
with other such systems. For example:
|
||||
|
||||
- SI decided to measure _length_ in meters, _mass_ in kilograms, and _time_ in seconds,
|
||||
- CGS decided to measure _length_ in centimeters, _mass_ in grams, and _time_ in seconds.
|
||||
|
||||
Both **systems of units** above agree on the unit of _time_, but chose different units for other
|
||||
quantities. In the above example, SI chose a non-prefixed unit of metre for a base quantity of _length_
|
||||
while CGS chose a scaled centimetre. On the other hand, SI chose a scaled kilogram over the gram used
|
||||
in the CGS. Those decisions also result in a need for different units for derived quantities.
|
||||
For example:
|
||||
|
||||
| Quantity | SI | CGS |
|
||||
|------------|---------------|-----------------|
|
||||
| _length_ | metre (m) | centimetre (cm) |
|
||||
| _mass_ | kilogram (kg) | gram (g) |
|
||||
| _time_ | second (s) | second (s) |
|
||||
| _force_ | newton (N) | dyne |
|
||||
| _energy_ | joule (J) | erg |
|
||||
| _pressure_ | pascal (Pa) | barye |
|
||||
|
||||
Often, there is no way to state which one is correct or which one is wrong. Each
|
||||
**system of units** has the freedom to choose whichever unit suits its engineering requirements
|
||||
and constraints the best.
|
||||
|
||||
## ISQ vs SI
|
||||
|
||||
Some of the systems of quantities and units have been used more over the years and have become more popular
|
||||
than others. Here are the official descriptions of the most popular systems used in engineering
|
||||
today:
|
||||
|
||||
!!! quote "[International System of Quantities (ISQ)](https://jcgm.bipm.org/vim/en/1.6.html)"
|
||||
|
||||
The **International System of Quantities (ISQ)** is a system of quantities based on the seven base
|
||||
quantities: _length_, _mass_, _time_, _electric current_, _thermodynamic temperature_,
|
||||
_amount of substance_, and _luminous intensity_.
|
||||
|
||||
|
||||
!!! quote "[International System of Units (SI)](https://jcgm.bipm.org/vim/en/1.16.html)"
|
||||
|
||||
The **International System of Units (SI)** is a system of units, based on the **International
|
||||
System of Quantities**, their names and symbols, including a series of prefixes and their names
|
||||
and symbols, together with rules for their use, adopted by the General Conference on Weights
|
||||
and Measures (CGPM).
|
||||
|
||||
|
||||
## The International System of Quantities (ISQ) standardization
|
||||
|
||||
The set of quantities constituting the ISQ is defined in the series of ISO 80000 and IEC 80000
|
||||
standards under the general title "Quantities and units".
|
||||
|
||||
ISO 80000:
|
||||
|
||||
- Part 1: General
|
||||
- Part 2: Mathematical signs and symbols to be used in the natural sciences and technology
|
||||
- Part 3: Space and time
|
||||
- Part 4: Mechanics
|
||||
- Part 5: Thermodynamics
|
||||
- Part 7: Light
|
||||
- Part 8: Acoustics
|
||||
- Part 9: Physical chemistry and molecular physics
|
||||
- Part 10: Atomic and nuclear physics
|
||||
- Part 11: Characteristic numbers
|
||||
- Part 12: Condensed matter physics
|
||||
|
||||
IEC 80000:
|
||||
|
||||
- Part 6: Electromagnetism
|
||||
- Part 13: Information science and technology
|
||||
- Part 15: Logarithmic and related quantities, and their units
|
||||
- Part 16: Printing and writing rules
|
||||
- Part 17: Time dependency
|
||||
|
||||
|
||||
## To be continued...
|
||||
|
||||
In the next part of this series, we will describe typical issues with libraries that do not
|
||||
model systems of quantities.
|
219
docs/blog/posts/isq-part-2-problems-when-isq-is-not-used.md
Normal file
219
docs/blog/posts/isq-part-2-problems-when-isq-is-not-used.md
Normal file
@ -0,0 +1,219 @@
|
||||
---
|
||||
draft: true
|
||||
date: 2024-10-14
|
||||
authors:
|
||||
- mpusz
|
||||
categories:
|
||||
- Metrology
|
||||
comments: true
|
||||
---
|
||||
|
||||
# International System of Quantities (ISQ): Part 2 - Problems when ISQ is not used
|
||||
|
||||
This article is the next one in our series about the ISQ. After introducing the basic terms and
|
||||
systems, in this article, we will talk about the benefits we get from modeling it in our library.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
!!! note
|
||||
|
||||
The issues described in this article do not apply to the **mp-units** library. Its interfaces,
|
||||
even if when we decide only to use [simple quantities](../../users_guide/framework_basics/simple_and_typed_quantities.md)
|
||||
that only use units, those are still backed up by quantity kinds under the framework's hood._
|
||||
|
||||
## Articles from this series
|
||||
|
||||
Previous:
|
||||
|
||||
- [Part 1 - Introduction](isq-part-1-introduction.md)
|
||||
|
||||
|
||||
## Limitations of units-only solutions
|
||||
|
||||
Units-only is not a good design for a quantities and units library. It works to some extent, but
|
||||
plenty of use cases can't be addressed, and for those that somehow work, we miss important safety improvements provided by additional abstractions in this article.
|
||||
|
||||
### No way to specify a quantity type in generic interfaces
|
||||
|
||||
A common requirement in the domain is to write unit-agnostic generic interfaces. For example,
|
||||
let's try to implement a generic `avg_speed` function template that takes a quantity of any
|
||||
unit and produces the result. So if we call it with _distance_ in `km` and _time_ in `h`, we will
|
||||
get `km/h` as a result, but if we call it with `mi` and `h`, we expect `mi/h` to be returned.
|
||||
|
||||
```cpp
|
||||
template<Unit auto U1, typename Rep1, Unit auto U2, typename Rep2>
|
||||
auto avg_speed(quantity<U1, Rep1> distance, quantity<U2, Rep2> time)
|
||||
{
|
||||
return distance / time;
|
||||
}
|
||||
|
||||
quantity speed = avg_speed(120 * km, 2 * h);
|
||||
```
|
||||
|
||||
This function works but does not provide any type safety to the users. The function arguments
|
||||
can be easily reordered on the call site. Also, we do not get any information about the
|
||||
return type of the function or any safety measures to ensure that the function logic actually
|
||||
returns a quantity of _speed_.
|
||||
|
||||
To improve safety, with a units-only library, we have to write the function in the following way:
|
||||
|
||||
```cpp
|
||||
template<typename Rep1, typename Rep2>
|
||||
quantity<si::metre / si::second, decltype(Rep1{} / Rep2{})> avg_speed(quantity<si::metre, Rep1> distance,
|
||||
quantity<si::second, Rep2> time)
|
||||
{
|
||||
return distance / time;
|
||||
}
|
||||
|
||||
avg_speed(120 * km, 2 * h).in(km / h);
|
||||
```
|
||||
|
||||
Despite being safer, the above code decreased the performance because we always pay for the
|
||||
conversion at the function's input and output.
|
||||
|
||||
We could try to provide concepts like `ScaledUnitOf<si::metre>` that will try to constrain
|
||||
the arguments somehow, but it leads to even more problems with the unit definitions. For example,
|
||||
are `Hz` and `Bq` just scaled versions of `1/s`? What about radian and steradian or a litre and
|
||||
a cubic meter?
|
||||
|
||||
Moreover, in a good library, the above code should not compile. The reason for this is that
|
||||
even though the conversion from `km` to `m` and from `h` to `s` is considered value-preserving,
|
||||
it is not true in the opposite direction. When we try to convert the result stored in an
|
||||
integral type from the unit of `m/s` to `km/h`, we will inevitably lose some data.
|
||||
|
||||
|
||||
### Disjoint units of the same quantity type do not work
|
||||
|
||||
Sometimes, we need to define several units describing the same quantity but which do not convert
|
||||
to each other. A typical example can be a currency use case. A user may want to define EURO and
|
||||
USD as units of currency, but do not provide any predefined conversion factor and handle such
|
||||
a conversion at runtime with custom logic (e.g., using an additional time point function argument).
|
||||
In such a case, how can we specify that EURO and USD are quantities of the same type/dimension?
|
||||
|
||||
|
||||
## Dimensions to the rescue?
|
||||
|
||||
To prevent the above issues, most of the libraries on the market introduce dimension abstraction.
|
||||
Thanks to that, we could solve the first issue of the previous chapter with:
|
||||
|
||||
```cpp
|
||||
QuantityOf<dim_speed> auto avg_speed(QuantityOf<dim_length> auto distance,
|
||||
QuantityOf<dim_time> auto time)
|
||||
{
|
||||
return distance / time;
|
||||
}
|
||||
```
|
||||
|
||||
and the second one by specifying that both EURO and USD are units of `dim_currency`. This is
|
||||
a significant improvement but still has some issues.
|
||||
|
||||
|
||||
## Limitations of dimensions
|
||||
|
||||
Let's first look at the above solution again. A domain expert seeing this code will immediately
|
||||
say there is no such thing as a speed dimension. The ISQ specifies only 7 dimensions with
|
||||
unique symbols assigned, and the dimensions of all the ISQ quantities are created as a
|
||||
vector product of those. For example, a quantity of _speed_ has a dimension of $L^1T^{-1}$.
|
||||
So, to be physically correct, the above code should be rewritten as:
|
||||
|
||||
```cpp
|
||||
QuantityOf<dim_length / dim_time> auto avg_speed(QuantityOf<dim_length> auto distance,
|
||||
QuantityOf<dim_time> auto time)
|
||||
{
|
||||
return distance / time;
|
||||
}
|
||||
```
|
||||
|
||||
Most of the libraries on the market ignore this fact and try to model distinct quantities through
|
||||
their dimensions, giving a false sense of safety. A dimension is not enough to describe a quantity.
|
||||
This has been known for a long time now. The ["Measurement Data (Archive Report)"](https://www.bkent.net/Doc/mdarchiv.pdf)
|
||||
report from 1996 says explicitly:
|
||||
|
||||
!!! quote "[Measurement Data (Archive Report)](https://www.bkent.net/Doc/mdarchiv.pdf)"
|
||||
|
||||
Dimensional analysis does not adequately model the semantics of measurement data.
|
||||
|
||||
In the following chapters, we will see a few use cases that can't be solved with an approach
|
||||
that only relies on units or dimensions.
|
||||
|
||||
### SI units of quantities of the same dimension but different kinds
|
||||
|
||||
The SI provides several units for distinct quantities of the same dimension but different kinds.
|
||||
For example:
|
||||
|
||||
- hertz (Hz) is a unit of _frequency_ and becquerel (Bq) is a unit of _activity_.
|
||||
Both are defined as $s^{-1}$, and have the same dimension of $T^{-1}$.
|
||||
- gray (Gy) is a unit of _absorbed dose_ and sievert (Sv) is a unit of _dose equivalent_.
|
||||
Both are defined as $m^2 s^{-2}$, and have the same dimension of $L^2T^{-2}$
|
||||
- radian (rad) is a unit of _plane angle_ defined as $m/m$, and
|
||||
steradian (sr) is a unit of _solid angle_ defined as $m^2/m^2$.
|
||||
Both are quantities of dimension one, which also has its own units like one (1) and percent (%).
|
||||
|
||||
There are many more similar examples in the ISO 80000 series. For example, _storage capacity_
|
||||
quantity can be measured in units of one, bit, octet, and byte.
|
||||
|
||||
The above conflicts can't be solved with dimensions, and they yield many safety issues. For example,
|
||||
we can ask ourselves what should be the result of the following:
|
||||
|
||||
1. `quantity q = 1 * Hz + 1 * Bq;`
|
||||
2. `quantity<Gy> q = 42 * Sv;`
|
||||
3. `bool b = (1 * rad + 1 * bit) == 2 * sr;`
|
||||
|
||||
None of the above code should compile, but most of the libraries on the market happily accept it
|
||||
and provide meaningless results. Some of them decide not to define one or more of the above
|
||||
units at all to avoid potential safety issues. For example,
|
||||
[the Au library does not define Sv to avoid mixing it up with Gy](https://github.com/aurora-opensource/au/pull/157).
|
||||
|
||||
### Derived quantities of the same dimension but different kinds
|
||||
|
||||
Even if some quantities do not have a specially assigned unit, they may still have a totally
|
||||
different physical meaning even if they share the same dimension:
|
||||
|
||||
- _work_ vs. _moment of force_ both of the same dimension $L^2MT^{-2}$,
|
||||
- _fuel consumption_ expressed in $\frac{l}{100\;km}$ vs. _area_ expressed in $m^2$ both of the same
|
||||
dimension $L^2$.
|
||||
|
||||
Again, we don't want to accidentally mix those.
|
||||
|
||||
### Various quantities of the same dimension and kinds
|
||||
|
||||
Even if we somehow address all the above, there are still plenty of use cases that still can't be
|
||||
safely implemented with such abstractions.
|
||||
|
||||
Let's consider that we want to implement a freight transport application to position cargo in the
|
||||
container. In such a scenario, we need to be able to discriminate between _length_, _width_, and
|
||||
_height_ of the package. Also, often, we can find a "This side up" arrow on the box.
|
||||
|
||||
A similar but also really important use case is in aviation. The current _altitude_ is a totally
|
||||
different quantity than the _distance_ to the destination. The same is true for _forward speed_
|
||||
and _sink rate_. We do not want to accidentally mix those.
|
||||
|
||||
When we deal with _energy_, we should be able to implicitly construct it from a proper product of
|
||||
any _mass_, _length_, and _time_. However, when we want to calculate _gravitational potential energy_,
|
||||
we may not want it to be implicitly initialized from any expression of matching dimensions.
|
||||
Such an implicit construction should be allowed only if we multiply a _mass_ with
|
||||
_acceleration of free fall_ and _height_. All other conversions should have an explicit annotation
|
||||
to make it clear that something potentially unsafe is being done in the code. Also, we should not
|
||||
be able to assign a _potential energy_ to a quantity of _kinetic energy_. However, both of them
|
||||
(possibly accumulated with each other) should be convertible to a _mechanical energy_ quantity.
|
||||
|
||||
Yet another example comes from the audio industry. In the audio software, we want to treat specific
|
||||
counts (e.g., _beats_, _samples_) as separate quantities. We could assign dedicated base dimensions
|
||||
to them. However, if we divide them by _duration_, we should obtain a quantity convertible to
|
||||
_frequency_ and even be able to express the result in a unit of `Hz`. With the dedicated dimensions
|
||||
approach, this wouldn't work as the dimension of frequency is just $T^{-1}$, which would not match
|
||||
the results of our dimensional equations. This is why we can't assign dedicated dimensions to such
|
||||
counts.
|
||||
|
||||
The last example that we want to mention here comes from finance. This time, we need to model _volume_
|
||||
as a special quantity of _currency_. _volume_ can be obtained by multiplying _currency_ by the
|
||||
dimensionless _market quantity_. Of course, both _currency_ and _volume_ should be expressed in
|
||||
the same units (e.g., USD).
|
||||
|
||||
None of the above scenarios can be addressed with just units and dimensions. We need a better
|
||||
abstraction to safely implement them.
|
||||
|
||||
## To be continued...
|
||||
|
||||
In the next part of this series, we will introduce the main ideas behind the International
|
||||
System of Quantities and provide solutions to the problems described above.
|
427
docs/blog/posts/isq-part-3-modelling-isq.md
Normal file
427
docs/blog/posts/isq-part-3-modelling-isq.md
Normal file
@ -0,0 +1,427 @@
|
||||
---
|
||||
draft: true
|
||||
date: 2024-10-21
|
||||
authors:
|
||||
- mpusz
|
||||
categories:
|
||||
- Metrology
|
||||
comments: true
|
||||
---
|
||||
|
||||
# International System of Quantities (ISQ): Part 3 - Modelling ISQ
|
||||
|
||||
The physical units libraries on the market typically only focus on modeling one or more systems
|
||||
of units. However, as we have learned, this is not the only system kind to model. Another,
|
||||
and maybe even more important, is a system of quantities. The most important example here is
|
||||
the International System of Quantities (ISQ) defined by ISO/IEC 80000.
|
||||
|
||||
This article continues our series about the International System of Quantities. This time, we will
|
||||
learn about the main ideas behind the ISQ and describe how it can be modelled in a programming
|
||||
language.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
## Articles from this series
|
||||
|
||||
Previous:
|
||||
|
||||
- [Part 1 - Introduction](isq-part-1-introduction.md)
|
||||
- [Part 2 - Problems when ISQ is not used](isq-part-2-problems-when-isq-is-not-used.md)
|
||||
|
||||
|
||||
## 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 _work_ and _torque_.
|
||||
Both of those have the same dimension but are different quantities.
|
||||
|
||||
A similar issue is related to figuring out 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.
|
||||
|
||||
If the above example seems too abstract, let's consider _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.
|
||||
|
||||
!!! important
|
||||
|
||||
More than one quantity may be defined for the same dimension:
|
||||
|
||||
- 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_, ...)
|
||||
|
||||
It turns out that the above issues can't be solved correctly without proper modeling of
|
||||
a [system of quantities](../../appendix/glossary.md#system-of-quantities).
|
||||
|
||||
|
||||
## Quantities of the same kind
|
||||
|
||||
As it was described in the previous article, dimension is not enough to describe a quantity.
|
||||
We need a better abstraction to ensure the safety of our calculations.
|
||||
|
||||
The ISO 80000-1:2009 says:
|
||||
|
||||
!!! quote "ISO 80000-1:2009"
|
||||
|
||||
- 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**.
|
||||
|
||||
ISO Guide also explicitly states:
|
||||
|
||||
!!! quote "ISO Guide"
|
||||
|
||||
**Measurement units of quantities of the same quantity dimension may be designated by the same
|
||||
name and symbol even when the quantities are not of the same kind**. For example,
|
||||
joule per kelvin and J/K are respectively the name and symbol of both a measurement unit of
|
||||
_heat capacity_ and a measurement unit of _entropy_, which are generally not considered to be
|
||||
quantities of the same kind. **However, in some cases special measurement unit names are
|
||||
restricted to be used with quantities of specific kind only**. For example, the measurement
|
||||
unit ‘second to the power minus one’ (1/s) is called hertz (Hz) when used for _frequencies_
|
||||
and becquerel (Bq) when used for _activities of radionuclides_. As another example, the joule
|
||||
(J) is used as a unit of _energy_, but never as a unit of _moment of force_,
|
||||
i.e. the newton metre (N · m).
|
||||
|
||||
|
||||
The above quotes from ISO provide answers to all the issues mentioned above and in the previous
|
||||
article.
|
||||
|
||||
More than one quantity may be defined for the same dimension:
|
||||
|
||||
- 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_)
|
||||
|
||||
Two quantities can't be added, subtracted, or compared unless they belong to
|
||||
the same [kind](../../appendix/glossary.md#kind). As _frequency_, _activity_, and _modulation rate_
|
||||
are of different kinds, the expression provided above should not compile.
|
||||
|
||||
|
||||
## System of quantities is not only about kinds
|
||||
|
||||
ISO/IEC 80000 specifies hundreds of different quantities. Plenty of various kinds are provided,
|
||||
and often, each kind contains more than one quantity. 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-1:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
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 --- position_vector["<b>position_vector</b><br>{vector}"]
|
||||
length --- displacement["<b>displacement</b><br>{vector}"]
|
||||
radius --- radius_of_curvature["<b>radius_of_curvature</b>"]
|
||||
```
|
||||
|
||||
Each of the above quantities expresses some kind of _length_, and each can be measured with meters,
|
||||
which is the unit defined by the SI for quantities of _length_. However, each has different
|
||||
properties, usage, and sometimes even a different character (_position vector_ and _displacement_
|
||||
are vector quantities).
|
||||
|
||||
Forming such a hierarchy helps us define arithmetics and conversion rules for various
|
||||
quantities of the same kind.
|
||||
|
||||
|
||||
## Converting between quantities of the same kind
|
||||
|
||||
Based on the hierarchy above, we can define the following 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));
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
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));
|
||||
```
|
||||
|
||||
Explicit conversions are forced by passing the quantity to a call operator of a `quantity_spec`
|
||||
type:
|
||||
|
||||
```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
|
||||
foo(isq::height(q1)); // explicit quantity conversion
|
||||
```
|
||||
|
||||
3. **Explicit casts**
|
||||
|
||||
- _height_ is never a _width_, and vice versa.
|
||||
- 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));
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
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));
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
|
||||
## Comparing, adding, and subtracting quantities of the same kind
|
||||
|
||||
ISO/IEC 80000 explicitly states that _width_ and _height_ are quantities of the same kind,
|
||||
and as such they:
|
||||
|
||||
- are mutually comparable, and
|
||||
- can be added and subtracted.
|
||||
|
||||
This means that we should be allowed to compare any quantities from the same tree (as long as
|
||||
their underlying representation types are comparable):
|
||||
|
||||
```cpp
|
||||
static_assert(isq::radius(1 * m) == isq::height(1 * m));
|
||||
```
|
||||
|
||||
Also, based on our hierarchy above, 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 node in a hierarchy tree of the same kind.
|
||||
For example:
|
||||
|
||||
```cpp
|
||||
static_assert((isq::width(1 * m) + isq::height(1 * m)).quantity_spec == isq::length);
|
||||
static_assert((isq::thickness(1 * m) + isq::radius(1 * m)).quantity_spec == isq::width);
|
||||
static_assert((isq::distance(1 * m) + isq::path_length(1 * m)).quantity_spec == isq::path_length);
|
||||
```
|
||||
|
||||
|
||||
## Modeling a quantity kind
|
||||
|
||||
In the quantities and 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>`.
|
||||
|
||||
!!! important
|
||||
|
||||
`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 quantity 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>);
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
||||
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. 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>);
|
||||
```
|
||||
|
||||
|
||||
## How do systems of units benefit from the ISQ and quantity kinds?
|
||||
|
||||
Modeling a system of units is the most essential 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.
|
||||
|
||||
Probably all the libraries in the wild model the SI, or at least most of it, and many of them
|
||||
provide support for additional units belonging to various other systems (e.g., imperial).
|
||||
|
||||
### Systems of units are based on systems of quantities
|
||||
|
||||
Systems of quantities specify a set of quantities and equations relating to those quantities.
|
||||
Those equations do not take any unit or a numerical representation into account at all. In order
|
||||
to create a quantity, we need to add those missing pieces of information. This is where
|
||||
a system of units kicks in.
|
||||
|
||||
The SI is explicitly stated to be based on the ISQ. Among others, it defines seven base units,
|
||||
one for each base quantity of ISQ. In the library, this is expressed by associating a quantity
|
||||
kind to a unit being defined:
|
||||
|
||||
```cpp
|
||||
inline constexpr struct metre final : named_unit<"m", kind_of<isq::length>> {} metre;
|
||||
```
|
||||
|
||||
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 on it) can be used to express
|
||||
the amount of any quantity of kind _length_.
|
||||
|
||||
!!! note
|
||||
|
||||
For some systems of units (e.g., natural units), a unit may not have an associated quantity
|
||||
type. For example, if we define the speed of light constant as `c = 1`, we can define a system
|
||||
where both _length_ and _time_ will be measured in seconds, and _speed_ will be a quantity
|
||||
measured with the unit `one`. In such case, the definition will look as follows:
|
||||
|
||||
```cpp
|
||||
inline constexpr struct second final : named_unit<"s"> {} second;
|
||||
```
|
||||
|
||||
### Constraining a derived unit to work only with a specific derived quantity
|
||||
|
||||
Some derived units are valid only for specific derived quantities. For example, SI specifies
|
||||
both hertz and becquerel derived units with the same unit equation $s^{-1}$. However, it also
|
||||
explicitly states:
|
||||
|
||||
!!! quote "SI"
|
||||
|
||||
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.
|
||||
|
||||
This is why it is important for the library to allow constraining such units to be used only with
|
||||
a specific quantity kind:
|
||||
|
||||
```cpp
|
||||
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;
|
||||
```
|
||||
|
||||
With the above, `hertz` can only be used for _frequencies_, while `becquerel` should only be used
|
||||
for quantities of _activity_:
|
||||
|
||||
```cpp
|
||||
quantity<isq::frequency[Hz]> q1 = 60 * Bq; // Compile-time error
|
||||
quantity<isq::activity[Hz]> q2; // Compile-time error
|
||||
quantity<isq::frequency[Hz]> q3 = 60 * Hz;
|
||||
std::cout << q3.in(Bq) << "\n"; // Compile-time error
|
||||
```
|
||||
|
||||
We know already that quantities of different kinds can't be compared, added, and subtracted.
|
||||
The following equation will not compile thanks to constraining derived units to be valid for
|
||||
specific kinds only:
|
||||
|
||||
```cpp
|
||||
auto q = 1 * Hz + 1 * Bq; // Fails to compile
|
||||
```
|
||||
|
||||
All of the above features improve the safety of our library and the products that use it.
|
||||
|
||||
|
||||
## To be continued...
|
||||
|
||||
In the next part of this series, we will discuss the challenges and issues related to the modelling
|
||||
of the ISQ with a programming language.
|
Reference in New Issue
Block a user