mirror of
https://github.com/mpusz/mp-units.git
synced 2025-07-29 18:07:16 +02:00
docs: "Introducing absolute quantities" blog post added
This commit is contained in:
160
docs/blog/posts/introducing-absolute-quantities.md
Normal file
160
docs/blog/posts/introducing-absolute-quantities.md
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
---
|
||||||
|
date: 2025-06-16
|
||||||
|
authors:
|
||||||
|
- mpusz
|
||||||
|
categories:
|
||||||
|
- Metrology
|
||||||
|
comments: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Introducing absolute quantities
|
||||||
|
|
||||||
|
This post introduces a new abstraction called an absolute quantity. It complements affine
|
||||||
|
space abstractions (point and delta) and will most probably be a new default in the library
|
||||||
|
when we release V3.
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
## Affine space in a nutshell
|
||||||
|
|
||||||
|
So far **mp-units** and other quantities and units libraries have been modeling two kinds of
|
||||||
|
abstractions:
|
||||||
|
|
||||||
|
1. Points (modeled with `quantity_point` class template)
|
||||||
|
|
||||||
|
- can be interpolated,
|
||||||
|
- can be subtracted,
|
||||||
|
- can't be added,
|
||||||
|
- can't be multiplied or divided by another quantity or scalar,
|
||||||
|
- some of them could be specified as non-negative,
|
||||||
|
- specified relative to some absolute or relative point origin,
|
||||||
|
- conversion to offset units (e.g., degree Celsius) accounts for offset.
|
||||||
|
|
||||||
|
2. Deltas (modeled with `quantity` class template)
|
||||||
|
|
||||||
|
- can be interpolated,
|
||||||
|
- can be subtracted,
|
||||||
|
- can be added,
|
||||||
|
- can be multiplied and divided by another quantity and scalar,
|
||||||
|
- may be negative as there is always a chance that we will subtract a larger value
|
||||||
|
from a smaller one,
|
||||||
|
- not specified relative to any origin,
|
||||||
|
- conversion to offset units (e.g., degree Celsius) ignores the offset (just the conversion
|
||||||
|
factor is used).
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
|
||||||
|
More information on this subject can be found in
|
||||||
|
[the Affine Space chapter](../../users_guide/framework_basics/the_affine_space.md).
|
||||||
|
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
A current affine space implementation works well for many essential use cases. However,
|
||||||
|
it also produces some issues.
|
||||||
|
|
||||||
|
1. We can't print quantity points as, at least for today, we do not have the means to properly
|
||||||
|
describe the user-provided origin in the text output.
|
||||||
|
2. Quantity points are hard to use in physical equations to denote not-delta values.
|
||||||
|
|
||||||
|
To make both of the above work, a user needs to convert the quantity point to quantity with
|
||||||
|
either `qp.quantity_from_zero()` or `qp.quantity_from(some_origin)` member functions.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
quantity_point m1(2 * kg);
|
||||||
|
quantity_point m2(3 * kg);
|
||||||
|
quantity_point m = m1 + m2.quantity_from_zero();
|
||||||
|
quantity d_v = 30 * km / h;
|
||||||
|
quantity E_k = m.quantity_from_zero() * pow<2>(d_v) / 2;
|
||||||
|
|
||||||
|
std::cout << "Mass: " << m.quantity_from_zero() << "\n";
|
||||||
|
std::cout << "Velocity: " << d_v << "\n";
|
||||||
|
std::cout << "Kinetic energy: " << E_k.in<double>(J) << "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
This is quite inconvenient and is a common reason for avoiding quantity point abstraction
|
||||||
|
in many equations in source code where it would be a good fit otherwise.
|
||||||
|
|
||||||
|
|
||||||
|
## Absolute quantities
|
||||||
|
|
||||||
|
Despite the above drawbacks, affine space points are necessary to model some abstractions
|
||||||
|
(e.g., temperatures in degrees Celsius, tared mass measurements, altitudes above some
|
||||||
|
reference point, etc.) and do it really well. Constrained affine space arithmetic
|
||||||
|
(e.g., preventing accidental addition of points) also improves the safety of our programs.
|
||||||
|
This is why it is a very valuable abstraction and should be used even more often than now.
|
||||||
|
|
||||||
|
To improve the user experience and open the doors for new features in the future, we are
|
||||||
|
considering adding a third abstraction for absolute quantities. In terms of properties,
|
||||||
|
an absolute quantity will lie between points and deltas.
|
||||||
|
|
||||||
|
| Feature | Point | Absolute | Delta |
|
||||||
|
|--------------------------------|:----------------------:|:------------------------:|:----------------------:|
|
||||||
|
| **Interpolation** | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
|
| **Subtraction** | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
|
| **Addition** | :material-close-thick: | :white_check_mark: | :white_check_mark: |
|
||||||
|
| **Multiply/Divide** | :material-close-thick: | :white_check_mark: | :white_check_mark: |
|
||||||
|
| **May be non-negative** | :white_check_mark: | :white_check_mark: | :material-close-thick: |
|
||||||
|
| **Relative to origin** | Absolute & relative | Absolute (implicit only) | :material-close-thick: |
|
||||||
|
| **Can use offset units** | :white_check_mark: | :material-close-thick: | :white_check_mark: |
|
||||||
|
| **Conversion to offset units** | With offset | :material-close-thick: | No offset |
|
||||||
|
| **Text output** | :material-close-thick: | :white_check_mark: | :white_check_mark: |
|
||||||
|
|
||||||
|
As we can see above, absolute quantities have only two limitations, and both are connected
|
||||||
|
to the offset units' usage. They can't use those because they must remain absolute
|
||||||
|
instead of being measured relative to some custom origin.
|
||||||
|
|
||||||
|
It is also vital to notice that there will be no way to provide a custom origin for
|
||||||
|
absolute quantities, even if it is defined in terms of `absolute_point_origin`. Those
|
||||||
|
quantities are meant to model abstractions with well-established and unambiguous zero
|
||||||
|
origins. If we allow passing absolute point origins, we could define two quantities
|
||||||
|
of the same type measured according to different not-related origins, which would be
|
||||||
|
too confusing.
|
||||||
|
|
||||||
|
The above also allows us to print them, as we do not need any special text to describe
|
||||||
|
their origin.
|
||||||
|
|
||||||
|
|
||||||
|
## Interfaces refactoring
|
||||||
|
|
||||||
|
As I mentioned in my [previous post](bringing-quantity-safety-to-the-next-level.md#should-we-get-rid-of-a-quantity_point),
|
||||||
|
we are seriously considering removing `quantity_point` class template and replacing it with
|
||||||
|
a `quantity_spec` point wrapper. For example, `quantity_point<isq::altitude[m]>` will become
|
||||||
|
`quantity<point<isq::altitude[m]>>`.
|
||||||
|
|
||||||
|
I initially planned `quantity<isq::mass>` to be the same as `quantity<delta<isq::mass>>`,
|
||||||
|
but it turns out that deltas probably should not be the default. It is consistent with how we
|
||||||
|
write physical expressions on paper, right? Delta symbol (∆) is always "verbose"
|
||||||
|
in our equations, it would be nice for the C++ code to do the same. So, deltas will always
|
||||||
|
need to be explicit.
|
||||||
|
|
||||||
|
And this brings us to absolute quantities. They should actually be the default we are looking
|
||||||
|
for. This is what we write as quantities in most of the physical equations. This is why we
|
||||||
|
will not need any specifier to denote them.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
quantity<point<isq::mass>> m1(10 * kg); // point quantity with an implicit point origin
|
||||||
|
quantity<isq::mass> m2 = 15 * kg; // absolute quantity (e.g., non-negative)
|
||||||
|
quantity<delta<isq::mass>> m3 = m1 - m2; // delta quantity (e.g., may be negative)
|
||||||
|
```
|
||||||
|
|
||||||
|
With the above, the previous examples may be refactored to:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
quantity m1 = 2 * kg;
|
||||||
|
quantity m2 = 3 * kg;
|
||||||
|
quantity m = m1 + m2;
|
||||||
|
quantity d_v = delta<km / h>(30);
|
||||||
|
quantity E_k = m * pow<2>(d_v) / 2;
|
||||||
|
|
||||||
|
std::cout << "Mass: " << m << "\n";
|
||||||
|
std::cout << "Velocity: " << d_v << "\n";
|
||||||
|
std::cout << "Kinetic energy: " << E_k.in<double>(J) << "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
We believe it will be a major improvement in the library. We plan to deliver the above
|
||||||
|
features as a part of **mp-units** V3.
|
||||||
|
|
||||||
|
Please share your feedback.
|
Reference in New Issue
Block a user