From c73ed97585eca4c7e78534990bc61b517ad52912 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 10 Sep 2020 21:16:38 +0200 Subject: [PATCH] docs: custom systems section extended --- docs/faq.rst | 12 ++++ docs/framework/units.rst | 22 ++++--- docs/use_cases/extensions.rst | 106 ++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 7 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index d17f8da8..f16191e7 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -30,6 +30,18 @@ different dimensions (i.e. height, width, and depth) all of them will just be measured in meters. +Why other systems are defined in the `si` namespace? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +All systems defined in the `si` namespace are defined in terms of base units +that are convertible to the :term:`SI` units. This enables conversions of +units of the same physical dimension between different systems. + +.. seealso:: + + More details on this subject can be found in the :ref:`Custom Systems` chapter. + + Why a dimensionless quantity is not just an fundamental arithmetic type? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/framework/units.rst b/docs/framework/units.rst index 3b4489e1..1489e184 100644 --- a/docs/framework/units.rst +++ b/docs/framework/units.rst @@ -64,9 +64,10 @@ definitions of prefixed units using `si::metre` as a reference (i.e. It is important to notice here that :term:`SI` is not the only system used in the industry and the library has to support other systems too. Also -in some cases conversions between such systems should be allowed. The -fact that the `base_dimension` takes the base unit in its definition makes -it really easy to define other systems of units. For example length in the +in some cases conversions between such systems should be allowed. Thanks to +the fact that the `base_dimension` takes the base unit type in its definition +allows us to easily define various systems of units for the same physical +dimension. For example length in the `CGS `_ could be defined as:: @@ -79,10 +80,17 @@ could be defined as:: The fact that both base dimensions use the same identifier ``"L"`` tells the library that both definitions refer to the same physical dimension of length. The only difference is the measurement unit used to define their -base dimensions. Thanks to using `si::centimetre` in the `si::cgs::dim_length` -definition we also enabled the ability to easily convert between those -2 base dimensions (as the library knows how to convert `si::metre` to -`si::centimetre` and vice versa). +base dimensions. Thanks to using the unit that is defined in terms of the +the same reference unit as the one provided to `si::dim_length` definition +(namely `si::centimetre` which is ``1/100`` of `si::metre`) we also enabled +the ability to easily convert between those 2 base dimensions (as the library +knows how to convert `si::metre` to `si::centimetre` and vice versa). + +.. seealso:: + + More details on custom systems definitions and conversions between + units of the same physical dimension can be found in the + :ref:`Custom Systems` chapter. Derived Units diff --git a/docs/use_cases/extensions.rst b/docs/use_cases/extensions.rst index b36e11a0..77f62ea1 100644 --- a/docs/use_cases/extensions.rst +++ b/docs/use_cases/extensions.rst @@ -199,6 +199,112 @@ In **mp-units** library a custom system can either be constructed from unique/new custom base dimensions or reuse dimensions of other systems. This allows extending, mixing, reuse, and interoperation between different systems. +Systems can be defined as standalone or provide interoperability and conversions +with other systems. It is up to the user to decide which one fits better to the +requirements. + +A standalone system is the one that has unique :term:`base units `. +Such units do not share their references with base units of other systems: + +.. code-block:: + :emphasize-lines: 3, 6 + + namespace fps { + + struct foot : named_unit {}; + struct yard : named_scaled_unit {}; + + struct dim_length : base_dimension<"L", foot> {}; + + template + using length = quantity; + + } // namespace fps + +If the base unit of one system has the same reference as the base unit of +another system, and both systems share the same base dimension symbol, the +library will enable conversions between units of those dimensions in +different systems: + +.. code-block:: + :emphasize-lines: 3, 6, 13, 16 + + namespace si { + + struct metre : named_unit {}; + struct kilometre : prefixed_unit {}; + + struct dim_length : base_dimension<"L", metre> {}; + + template + using length = quantity; + + namespace fps { + + struct foot : named_scaled_unit {}; + struct yard : named_scaled_unit {}; + + struct dim_length : base_dimension<"L", foot> {}; + + template + using length = quantity; + + } // namespace fps + } // namespace si + +Having the above two systems we can try the following code:: + + constexpr auto fps_yard = fps::length(1.); + std::cout << quantity_cast(fps_yard) << "\n"; // compile-time error + + constexpr auto si_fps_yard = si::fps::length(1.); + std::cout << quantity_cast(si_fps_yard) << "\n"; // prints "0.0009144 km" + +Even though the base dimension of ``si::fps`` is defined in terms of +``si::metre`` foot is preserved as the base unit of length in both systems:: + + constexpr auto fps_yard = fps::length(1.); + constexpr auto fps_area = quantity_cast(fps_yard * fps_yard); + std::cout << fps_yard << "\n"; // 1 yd + std::cout << fps_area << "\n"; // 9 ft² + + constexpr auto si_fps_yard = si::fps::length(1.); + constexpr auto si_fps_area = quantity_cast(si_fps_yard * si_fps_yard); + std::cout << si_fps_yard << "\n"; // 1 yd + std::cout << si_fps_area << "\n"; // 9 ft² + +In most cases we want conversions between systems and that is why nearly all +systems provided with this library are implemented in terms on the :term:`SI` +system. However, such an approach has also its problems. Let's see the +following simple application using the above defined systems:: + + std::ostream& operator<<(std::ostream& os, const ratio& r) + { + return os << "ratio{" << r.num << ", " << r.den << ", " << r.exp << "}"; + } + + std::ostream& operator<<(std::ostream& os, const Unit auto& u) + { + using unit_type = std::remove_cvref_t; + return os << unit_type::ratio << " x " << unit_type::reference::symbol.standard(); + } + + int main() + { + std::cout << "fps: " << fps::yard() << "\n"; // fps: ratio{3, 1, 0} x ft + std::cout << "si::fps: " << si::fps::yard() << "\n"; // si::fps: ratio{1143, 125, -1} x m + } + +As we can see, even though ``si::fps::yard`` is defined as ``3`` feet, +the library always keeps the ratio relative to the primary reference unit +which in this case is ``si::metre``. This results in much bigger ratios +and in case of some units may result with a problem of limited resolution +of ``std::int64_t`` used to store numerator, denominator, and exponent +values of ratio. For example the ``si::fps::qubic_foot`` already has the +ratio of ``ratio{55306341, 1953125, -3}``. In case of more complicated +conversion ratio we can overflow `ratio` and get a compile-time error. +In such a situation the standalone system may be a better choice here. + .. seealso::