From 2f7ecbc19d46eb68bc1a514806cac2876a7cc2a8 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Mon, 10 Jul 2023 17:56:05 +0200 Subject: [PATCH] docs: "Dimensionless Quantities" chapter added --- .../dimensionless_quantities.md | 248 ++++++++++++++++++ 1 file changed, 248 insertions(+) diff --git a/docs/users_guide/framework_basics/dimensionless_quantities.md b/docs/users_guide/framework_basics/dimensionless_quantities.md index e69de29b..1d45592d 100644 --- a/docs/users_guide/framework_basics/dimensionless_quantities.md +++ b/docs/users_guide/framework_basics/dimensionless_quantities.md @@ -0,0 +1,248 @@ +# Dimensionless Quantities + +The quantities we discussed so far always had some specific type and physical dimension. +However, this is not always the case. While performing various computations, we sometimes end up with +so-called "dimensionless" quantities, which ISO correctly defines as +[quantities of dimension one](../../../appendix/glossary/#dimensionless-quantity): + +!!! quote "ISO/IEC Guide 99" + + - Quantity for which all the exponents of the factors corresponding to the base quantities in + its quantity dimension are zero. + - The measurement units and values of quantities of dimension one are numbers, but such quantities + convey more information than a number. + - Some quantities of dimension one are defined as the ratios of two quantities of the same kind. + - Numbers of entities are quantities of dimension one. + + +## Dividing two quantities of the same kind + +Dividing two quantities of the same kind always results in a +[quantity of dimension one](../../../appendix/glossary/#dimensionless-quantity). +However, depending on what type of quantities we divide or what their units are, we may end up +with slightly different results. + +!!! note + + In **mp-units**, dividing two quantities of the same dimension always results in a quantity + with the dimension being `dimension_one`. This is often different for other physical units + libraries, which may return a raw representation type for such cases. A raw value is also always + returned from the division of two `std::chrono::duration` objects. + + To read more about the reasoning for this design decision, please check our + [FAQ](../../../getting_started/faq/#why-a-dimensionless-quantity-is-not-just-a-fundamental-arithmetic-type). + + +### Dividing quantities of the same type + +First, let's analyze what happens if we divide two quantities of the same type: + +```cpp +constexpr QuantityOf auto q = isq::height(200 * m) / isq::height(50 * m); +``` + +In such a case, we end up with a dimensionless quantity that has the following properties: + +```cpp +static_assert(q.quantity_spec == dimensionless); +static_assert(q.dimension == dimension_one); +static_assert(q.unit == one); +``` + +In case we would like to print its value, we would see a raw value of `4` in the output with no unit +being printed. + + +### Dividing quantities of different types + +Now let's see what happens if we divide quantities of the same dimension and unit but which have +different quantity types: + +```cpp +constexpr QuantityOf auto q = isq::work(200 * J) / isq::heat(50 * J); +``` + +Again we end up with `dimension_one` and `one`, but this time: + +```cpp +static_assert(q.quantity_spec == isq::work / isq::heat); +``` + +As shown above, the result is not of a `dimensionless` type anymore. Instead, we get a quantity type +derived from the performed [quantity equation](../../../appendix/glossary/#quantity-equation). +According to the [ISQ](../../../appendix/glossary/#isq), work divided by heat is the recipe for +the thermodynamic efficiency quantity, thus: + +```cpp +static_assert(implicitly_convertible(q.quantity_spec, isq::efficiency_thermodynamics)); +``` + +!!! note + + The quantity of `isq::efficiency_thermodynamics` is of a kind `dimensionless`, so it is implicitly + convertible to `dimensionless` and satisfies the `QuantityOf` concept. + + +### Dividing quantities of different units + +Now, let's see what happens when we divide two quantities of the same type but different units: + +```cpp +constexpr QuantityOf auto q = isq::height(4 * km) / isq::height(2 * m); +``` + +This time we still get a quantity of `dimensionless` type with a `dimension_one` as its dimension. +However, the resulting unit is not `one` anymore: + +```cpp +static_assert(q.unit == mag_power<10, 3> * one); +``` + +In case we would print the text output of this quantity, we would not see a raw value of `2000`, +but `2 km/m`. + +First, it may look surprising, but this is actually consistent with the division of quantities +of different dimensions. For example, if we divide `4 * km / 2 * s`, we do not expect `km` to be +"expanded" to `m` before the division, right? We would expect the result of `2 km/s`, which is +exactly what we get when we divide quantities of the same kind. + +This is a compelling feature that allows us to express huge or tiny ratios without the need +for big and expensive representation types. With this, we can easily define things like +a [Hubble's constant](https://en.wikipedia.org/wiki/Hubble%27s_law#Dimensionless_Hubble_constant) +that uses a unit that is proportional to the ratio of kilometers per megaparsecs, which are both +units of length: + +```cpp +inline constexpr struct hubble_constant : + named_unit * si::kilo / si::second / si::mega> {} hubble_constant; +``` + + +## Counts of things + +Another important use case for dimensionless quantities is to provide strong types for counts +of things. For example: + +- ISO-80000-3 provides a `rotation` quantity defined as the number of revolutions, +- IEC-80000-6 provides a `number_of_turns_in_a_winding` quantity, +- IEC-80000-13 provides a `Hamming_distance` quantity defined as the number of digit positions + in which the corresponding digits of two words of the same length are different. + +Thanks to assigning strong names to such quantities, later on they can be explicitly used as +arguments in the [quantity equations](../../../appendix/glossary/#quantity-equation) of other +quantities deriving from them. + + +## Predefined units of the dimensionless quantity + +As we observed above, the most common unit for dimensionless quantities is `one`. It has the +ratio of `1` and does not output any textual symbol. + +!!! note + + A unit `one` is special in the entire type system of units as it is considered to be + [an identity operand in the unit expression templates](../interface_introduction/#identities). + This means that, for example: + + ```cpp + static_assert(one * one == one); + static_assert(one * si::metre == si::metre); + static_assert(si::metre / si::metre == one); + ``` + + The same is also true for `dimension_one` and `dimensionless` in the domains of dimensions + and quantity specifications. + + +Besides the unit `one`, there are a few other scaled units predefined in the library for usage +with dimensionless quantities: + +```cpp +inline constexpr struct percent : named_unit<"%", mag * one> {} percent; +inline constexpr struct per_mille : named_unit * one> {} per_mille; +``` + + +## Angular quantities + +Special, often controversial, examples of dimensionless quantities are an angular measure +and solid angular measure quantities that are defined in the [ISQ](../../../appendix/glossary/#isq) +to be the result of a division of `arc_length / radius` and `area / pow<2>(radius)` respectively. +Moreover, [ISQ](../../../appendix/glossary/#isq) also explicitly states that both can be +expressed in the unit `one`. This means that both `isq::angular_measure` and `isq::solid_angular_measure` +should be of a [kind](../../../appendix/glossary/#kind) of `dimensionless`. + +On the other hand, [ISQ](../../../appendix/glossary/#isq) also specifies that a unit `radian` can +be used for `isq::angular_measure`, and a unit `steradian` can be used for `isq::solid_angular_measure`. +Those should not be mixed or used to express other types of dimensionless quantities. This means +that both `isq::angular_measure` and `isq::solid_angular_measure` should also be +[quantity kinds](../../../appendix/glossary/#kind) by themselves. + +!!! note + + Many people claim that angle being a dimensionless quantity is a bad idea. There are + proposals submitted to make an angle a base quantity and `rad` to become a base unit. More on this + topic can be found in the ["Strong Angular System" chapter](../../defining_systems/strong_angular_system). + + +## Nested quantity kinds + +Angular quantities are not the only ones with such a "strange" behavior. Another, but a similar case +is a `storage_capacity` quantity specified in IEC-80000-13 that again allows expressing it in both +`one` and `bit` units. + +Those cases make dimensionless quantities an exceptional tree in the library. This is the only +[quantity hierarchy](../../../appendix/glossary/#quantity-hierarchy) that contains more than one +[quantity kind](../../../appendix/glossary/#kind) in its tree: + +```mermaid +flowchart TD + dimensionless["dimensionless\n[one]"] + dimensionless --- rotation + dimensionless --- efficiency + dimensionless --- angular_measure["angular_measure\n[rad]"] + angular_measure --- rotational_displacement + angular_measure --- phase_angle + dimensionless --- solid_angular_measure["solid_angular_measure\n[sr]"] + dimensionless --- drag_factor + dimensionless --- storage_capacity["storage_capacity\n[bit]"] --- equivalent_binary_storage_capacity + dimensionless --- ... +``` + +To provide such support in the library, we provided an `is_kind` specifier that can be appended +to the quantity specification: + +=== "C++20" + + ```cpp + inline constexpr struct angular_measure : quantity_spec {} angular_measure; + inline constexpr struct solid_angular_measure : quantity_spec(radius), is_kind> {} solid_angular_measure; + inline constexpr struct storage_capacity : quantity_spec {} storage_capacity; + ``` + +=== "C++23" + + ```cpp + inline constexpr struct angular_measure : quantity_spec {} angular_measure; + inline constexpr struct solid_angular_measure : quantity_spec(radius), is_kind> {} solid_angular_measure; + inline constexpr struct storage_capacity : quantity_spec {} storage_capacity; + ``` + +=== "Portable" + + ```cpp + QUANTITY_SPEC(angular_measure, dimensionless, arc_length / radius, is_kind); + QUANTITY_SPEC(solid_angular_measure, dimensionless, area / pow<2>(radius), is_kind); + QUANTITY_SPEC(storage_capacity, dimensionless, is_kind); + ``` + +With the above, we can constrain `radian`, `steradian`, and `bit` to be allowed for usage with +specific quantity kinds only: + +```cpp +inline constexpr struct radian : named_unit<"rad", metre / metre, kind_of> {} radian; +inline constexpr struct steradian : named_unit<"sr", square(metre) / square(metre), kind_of> {} steradian; +inline constexpr struct bit : named_unit<"bit", one, kind_of> {} bit; +``` + +but still allow a usage of `one` and its scaled versions for such quantities.