docs: 3 first articles of the ISQ series added

This commit is contained in:
Mateusz Pusz
2024-09-30 18:25:03 +02:00
parent 6ffe2d022a
commit 189e75b3c2
3 changed files with 791 additions and 0 deletions

View 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.

View 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.

View 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.