From 189e75b3c24f245eb0486ae5569e50a6763b497f Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Mon, 30 Sep 2024 18:25:03 +0200 Subject: [PATCH] docs: 3 first articles of the ISQ series added --- docs/blog/posts/isq-part-1-introduction.md | 145 ++++++ ...sq-part-2-problems-when-isq-is-not-used.md | 219 +++++++++ docs/blog/posts/isq-part-3-modelling-isq.md | 427 ++++++++++++++++++ 3 files changed, 791 insertions(+) create mode 100644 docs/blog/posts/isq-part-1-introduction.md create mode 100644 docs/blog/posts/isq-part-2-problems-when-isq-is-not-used.md create mode 100644 docs/blog/posts/isq-part-3-modelling-isq.md diff --git a/docs/blog/posts/isq-part-1-introduction.md b/docs/blog/posts/isq-part-1-introduction.md new file mode 100644 index 00000000..a709ec08 --- /dev/null +++ b/docs/blog/posts/isq-part-1-introduction.md @@ -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? + + + +## 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. diff --git a/docs/blog/posts/isq-part-2-problems-when-isq-is-not-used.md b/docs/blog/posts/isq-part-2-problems-when-isq-is-not-used.md new file mode 100644 index 00000000..f8328d0d --- /dev/null +++ b/docs/blog/posts/isq-part-2-problems-when-isq-is-not-used.md @@ -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. + + + +!!! 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 +auto avg_speed(quantity distance, quantity 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 +quantity avg_speed(quantity distance, + quantity 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` 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 auto avg_speed(QuantityOf auto distance, + QuantityOf 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 auto avg_speed(QuantityOf auto distance, + QuantityOf 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 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. diff --git a/docs/blog/posts/isq-part-3-modelling-isq.md b/docs/blog/posts/isq-part-3-modelling-isq.md new file mode 100644 index 00000000..59e0ecc4 --- /dev/null +++ b/docs/blog/posts/isq-part-3-modelling-isq.md @@ -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. + + + +## 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["length
[m]"] + length --- width["width / breadth"] + length --- height["height / depth / altitude"] + width --- thickness["thickness"] + width --- diameter["diameter"] + width --- radius["radius"] + length --- path_length["path_length"] + path_length --- distance["distance"] + distance --- radial_distance["radial_distance"] + length --- wavelength["wavelength"] + length --- position_vector["position_vector
{vector}"] + length --- displacement["displacement
{vector}"] + radius --- radius_of_curvature["radius_of_curvature"] +``` + +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> q); + ``` + + ```cpp + quantity> q1 = 42 * m; + quantity> 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> q); + ``` + + ```cpp + quantity> q1 = 42 * m; + quantity> 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> q); + ``` + + ```cpp + quantity> q1 = 42 * m; + quantity> q2 = quantity_cast(q1); // explicit quantity cast + foo(quantity_cast(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); + ``` + + ```cpp + quantity> q1 = 42 * s; // Compile-time error + foo(quantity_cast(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`. + +!!! important + + `isq::length` and `kind_of` 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::height)); +``` + +Additionally, the result of operations on quantity kinds is also a quantity kind: + +```cpp +static_assert(same_type / kind_of, kind_of>); +``` + +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 / isq::time, kind_of>); +static_assert(same_type / 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` 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); + ``` + + +## 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> {} metre; +``` + +The `kind_of` 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> {} hertz; +inline constexpr struct becquerel final : named_unit<"Bq", one / second, kind_of> {} becquerel; +``` + +With the above, `hertz` can only be used for _frequencies_, while `becquerel` should only be used +for quantities of _activity_: + +```cpp +quantity q1 = 60 * Bq; // Compile-time error +quantity q2; // Compile-time error +quantity 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.