docs: custom systems section extended

This commit is contained in:
Mateusz Pusz
2020-09-10 21:16:38 +02:00
parent 79558a6d72
commit c73ed97585
3 changed files with 133 additions and 7 deletions

View File

@@ -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?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -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 <https://en.wikipedia.org/wiki/Centimetre%E2%80%93gram%E2%80%93second_system_of_units>`_
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

View File

@@ -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 <base unit>`.
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<foot, "ft", no_prefix> {};
struct yard : named_scaled_unit<yard, "yd", no_prefix, ratio(3), foot> {};
struct dim_length : base_dimension<"L", foot> {};
template<Unit U, ScalableNumber Rep = double>
using length = quantity<dim_length, U, Rep>;
} // 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<metre, "m", units::physical::si::prefix> {};
struct kilometre : prefixed_unit<kilometre, units::physical::si::kilo, metre> {};
struct dim_length : base_dimension<"L", metre> {};
template<Unit U, ScalableNumber Rep = double>
using length = quantity<dim_length, U, Rep>;
namespace fps {
struct foot : named_scaled_unit<foot, "ft", no_prefix, ratio(3'048, 1'000, -1), metre> {};
struct yard : named_scaled_unit<yard, "yd", no_prefix, ratio(3), foot> {};
struct dim_length : base_dimension<"L", foot> {};
template<Unit U, ScalableNumber Rep = double>
using length = quantity<dim_length, U, Rep>;
} // namespace fps
} // namespace si
Having the above two systems we can try the following code::
constexpr auto fps_yard = fps::length<fps::yard>(1.);
std::cout << quantity_cast<si::kilometre>(fps_yard) << "\n"; // compile-time error
constexpr auto si_fps_yard = si::fps::length<si::fps::yard>(1.);
std::cout << quantity_cast<si::kilometre>(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<fps::yard>(1.);
constexpr auto fps_area = quantity_cast<unknown_coherent_unit>(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<si::fps::yard>(1.);
constexpr auto si_fps_area = quantity_cast<unknown_coherent_unit>(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<decltype(u)>;
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::