--- 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. ## 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(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` will become `quantity>`. I initially planned `quantity` to be the same as `quantity>`, 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 inline constexpr struct tare final : relative_point_origin {} tare; quantity> m1(10 * kg); // point quantity with an implicit point origin quantity> m2 = tare + 8 * kg; // point quantity with an explicit relative point origin quantity        m3 = 15 * kg; // absolute quantity (e.g., non-negative) quantity> m13 = m1 - m3; // delta quantity (e.g., may be negative) quantity> m23 = m2 - m3; // delta quantity (e.g., may be negative) ``` With the above, the initial example may be refactored to: ```cpp quantity m1 = 2 * kg; quantity m2 = 3 * kg; quantity m = m1 + m2; quantity d_v = delta(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(J) << "\n"; ``` ## Arithmetic Affine space arithmetic is well-defined. However, we are adding a new type to the library that lands between points and deltas. This is why we must agree on the arithmetic for all possible combinations. Let's try to define them here, assuming that points and absolute values share a common origin. !!! note If points and absolute values do not share a common absolute point origin the operation should fail to compile. ### Addition In case of addition, a more constrained type is preserved (except for adding two points, which is undefined): | Lhs \ Rhs | Point | Absolute | Delta | |--------------|:----------------------:|:--------:|:--------:| | **Point** | :material-close-thick: | Point | Point | | **Absolute** | Point | Absolute | Absolute | | **Delta** | Point | Absolute | Delta | ### Subtraction Subtraction is more tricky. To verify the logic below, it might be helpful to ask whether a result may be negative when two arguments are non-negative. If the answer is true, the result should be a delta quantity. | Lhs \ Rhs | Point | Absolute | Delta | |--------------|:----------------------:|:--------:|:--------:| | **Point** | Delta | Point | Point | | **Absolute** | Delta | Delta | Absolute | | **Delta** | :material-close-thick: | Delta | Delta | ## New opportunities The new syntax simplifies API as one `quantity` class template will now serve all quantity variations (possibly even more in the future). It also allows us to model quantities that were impossible to express before without some workarounds. For example, we can now correctly calculate Carnot engine efficiency with any of the following: ```cpp quantity temp_cold = 300. * K; quantity temp_hot = 500. * K; quantity carnot_eff_1 = 1. - temp_cold / temp_hot; quantity carnot_eff_2 = (temp_hot - temp_cold) / temp_hot; ``` In the above code, we can easily create absolute or delta values of temperatures and do arithmetics on them. Previously, we had to create deltas from both points artificially with: ```cpp quantity temp_cold = point(300.); quantity temp_hot = point(500.); quantity carnot_eff_1 = 1. - temp_cold.quantity_from_zero() / temp_hot.quantity_from_zero(); quantity carnot_eff_2 = (temp_hot - temp_cold) / temp_hot.quantity_from_zero(); ``` It worked but was far from being physically pure and pretty. ## Summary We believe that adding absolute quantities will be a major improvement in the library that will allow us to more correctly model physical equations making them terser and easier to understand at the same time. We plan to deliver the features mentioned in this post as a part of **mp-units** V3. Please share your feedback.