forked from mpusz/mp-units
Sphinx-based documentation added
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
.. namespace:: units
|
||||
|
||||
Basic Concepts
|
||||
==============
|
||||
|
||||
The most important concepts in the library are `Unit`, `Dimension`, and
|
||||
`Quantity`:
|
||||
|
||||
.. image:: /_static/img/design.png
|
||||
:align: center
|
||||
|
||||
`Unit` is a basic building block of the library. Every dimension works with
|
||||
a concrete hierarchy of units. Such hierarchy defines a reference unit and
|
||||
often a few scaled versions of it.
|
||||
|
||||
`Dimension` concept matches a dimension of either a base or derived quantity.
|
||||
`base_dimension` is instantiated with a unique symbol identifier and a base
|
||||
unit. `derived_dimension` is a list of exponents of either base or other
|
||||
derived dimensions.
|
||||
|
||||
`Quantity` is a concrete amount of a unit for a specified dimension with a
|
||||
specific representation.
|
||||
@@ -0,0 +1,16 @@
|
||||
.. namespace:: units
|
||||
|
||||
Conversions and Casting
|
||||
=======================
|
||||
|
||||
Implicit Conversions
|
||||
--------------------
|
||||
|
||||
constructors
|
||||
|
||||
Explicit Casting
|
||||
----------------
|
||||
|
||||
quantity_cast
|
||||
|
||||
Example of casting to a dimension's coherent unit.
|
||||
@@ -0,0 +1,178 @@
|
||||
.. namespace:: units
|
||||
|
||||
Dimensions
|
||||
==========
|
||||
|
||||
In the previous chapter we briefly introduced the notion of a physical
|
||||
:term:`dimension`. Now it is time to learn much more about this subject.
|
||||
Length, time, velocity, area, energy are only a few examples of physical
|
||||
dimensions.
|
||||
|
||||
Operations
|
||||
----------
|
||||
|
||||
Quantities of the same dimension can be easily added or subtracted with
|
||||
each other and the result will always be a quantity of the same dimension:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 3-4
|
||||
|
||||
Length auto dist1 = 2q_m;
|
||||
Length auto dist2 = 1q_m;
|
||||
Length auto res1 = dist1 + dist2;
|
||||
Length auto res2 = dist1 - dist2;
|
||||
|
||||
Additionally, we can always multiply or divide a quantity by a
|
||||
:term:`scalar` and in such a case the quantity's dimension will also
|
||||
not change:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 2-4
|
||||
|
||||
Length auto dist = 2q_m;
|
||||
Length auto res1 = dist * 2; // 4 m
|
||||
Length auto res2 = 3 * res1; // 12 m
|
||||
Length auto res3 = res2 / 2; // 6 m
|
||||
|
||||
However, if we try to multiply or divide quantities of the same or
|
||||
different dimensions, or we will divide a scalar by a quantity, we most
|
||||
probably will always end up in a quantity of a yet another dimension:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 4-6
|
||||
|
||||
Length auto dist1 = 2q_m;
|
||||
Length auto dist2 = 3q_m;
|
||||
Time auto dur1 = 2q_s;
|
||||
Area auto res1 = dist1 * dist2; // 6 m²
|
||||
Velocity auto res2 = dist1 / dur1; // 1 m/s
|
||||
Frequency auto res3 = 10 / dur1; // 5 Hz
|
||||
|
||||
However, please note that there is an exception from the above rule.
|
||||
In case we divide the same dimensions, or multiply by the inverted
|
||||
dimension, than we will end up with just a scalar type:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 4-5
|
||||
|
||||
Time auto dur1 = 10q_s;
|
||||
Time auto dur2 = 2q_s;
|
||||
Frequency auto fr1 = 5q_Hz;
|
||||
Scalar auto v1 = dur1 / dur2; // 5
|
||||
Scalar auto v2 = dur1 * fr1; // 50
|
||||
|
||||
|
||||
Base dimensions
|
||||
---------------
|
||||
|
||||
The quantities of base dimensions are called
|
||||
:term:`base quantities <base quantity>` which are the atomic building blocks
|
||||
of a :term:`system of quantities`. For example the The International System
|
||||
of Units (:term:`SI`) defines 7 of them: length, mass, time, electric
|
||||
current, thermodynamic temperature, substance, and luminous intensity.
|
||||
|
||||
To define a new base dimension the `base_dimension` class template is
|
||||
provided. For example the SI base dimension of length can be defined as::
|
||||
|
||||
namespace si {
|
||||
|
||||
struct dim_length : base_dimension<"L", metre> {};
|
||||
|
||||
}
|
||||
|
||||
In the above code sample ``"L"`` is an base dimension's unique identifier
|
||||
and `si::metre` is a :term:`base unit` of this base dimension. We can
|
||||
obtain those back easily with::
|
||||
|
||||
static_assert(si::dim_length::symbol == "L");
|
||||
static_assert(std::is_same_v<si::dim_length::base_unit, si::metre>);
|
||||
|
||||
|
||||
Derived dimensions
|
||||
------------------
|
||||
|
||||
The quantities of derived dimensions are called
|
||||
:term:`derived quantities <derived quantity>` and are derived from base
|
||||
quantities. This means that they are created by multiplying or dividing
|
||||
quantities of other dimensions.
|
||||
|
||||
Looking at the previous code snippets the area, velocity, or frequency are
|
||||
the examples of such quantities. Each derived quantity can be represented
|
||||
as a unique list of exponents of base quantities. For example:
|
||||
|
||||
- an area is a length base quantity raised to the exponent ``2``
|
||||
- a velocity is formed from the length base quantity with exponent ``1``
|
||||
and time base quantity with exponent ``-1``.
|
||||
|
||||
The above dimensions can be defined in the library with the
|
||||
`derived_dimension` class template as follows::
|
||||
|
||||
namespace si {
|
||||
|
||||
struct dim_area : derived_dimension<dim_area, square_metre,
|
||||
exp<dim_length, 2>> {};
|
||||
struct dim_velocity : derived_dimension<dim_velocity, metre_per_second,
|
||||
exp<dim_length, 1>, exp<dim_time, -1>> {};
|
||||
|
||||
}
|
||||
|
||||
In the above code sample `si::square_metre` and `si::metre_per_second`
|
||||
are the :term:`coherent derived units <coherent derived unit>` of those
|
||||
derived dimensions.
|
||||
|
||||
Coherent unit argument is followed by the list of exponents that form this
|
||||
derived dimension. This list is called a :term:`recipe` of this derived
|
||||
dimension and may contain both base and derived dimensions. In the latter
|
||||
case the dimension is being extracted to base dimensions by the framework
|
||||
itself. The order and types of dimensions used in the recipe determine how
|
||||
an dimension's unnamed unit symbol is being printed in the text output.
|
||||
|
||||
.. seealso::
|
||||
|
||||
More information on how the :term:`recipe` affect the printed symbol
|
||||
of unnamed unit can be found in the :ref:`Derived Unnamed Units` chapter.
|
||||
|
||||
It is important to mention here that beside text output the order and
|
||||
the number of elements in the `derived_dimension` definition does not
|
||||
matter. Even if we define the above as:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 4, 6
|
||||
|
||||
namespace si {
|
||||
|
||||
struct dim_area : derived_dimension<dim_area, square_metre,
|
||||
exp<dim_length, 1>, exp<dim_length, 1>> {};
|
||||
struct dim_velocity : derived_dimension<dim_velocity, metre_per_second,
|
||||
exp<dim_time, -1>, exp<dim_length, 1>> {};
|
||||
|
||||
}
|
||||
|
||||
the library will do its magic and will end up with the same
|
||||
:term:`normalized derived dimension` which will allow the dimensional
|
||||
analysis in the library to work as expected.
|
||||
|
||||
.. note::
|
||||
|
||||
The first template argument of `derived_dimension` is the type of the
|
||||
child class inherited from the instantiation of this `derived_dimension`
|
||||
class template. This is called a
|
||||
:abbr:`CRTP (Curiously Recurring Template Parameter)` Idiom and is used
|
||||
in many places in this library to provide :ref:`The Downcasting Facility`.
|
||||
Hopefully if [P0847]_ will land in C++23 the additional CRTP-related
|
||||
template parameter will be removed from this definition.
|
||||
|
||||
|
||||
Obtaining a Unit of the Dimension
|
||||
---------------------------------
|
||||
|
||||
In order to obtain the base/coherent unit of any dimension type a
|
||||
`dimension_unit` helper was introduced::
|
||||
|
||||
static_assert(std::is_same_v<dimension_unit<si::dim_length>, si::metre>);
|
||||
static_assert(std::is_same_v<dimension_unit<si::dim_velocity>, si::metre_per_second>);
|
||||
|
||||
|
||||
.. rubric:: Citations:
|
||||
|
||||
.. [P0847] `"Deducing this" <https://wg21.link/P0847>`_, Programming Language C++ proposal
|
||||
@@ -0,0 +1,159 @@
|
||||
.. namespace:: units
|
||||
|
||||
Quantities
|
||||
==========
|
||||
|
||||
A :term:`quantity` is a concrete amount of a unit for a specified dimension
|
||||
with a specific representation and is represented in the library with a
|
||||
`quantity` class template.
|
||||
|
||||
|
||||
Construction
|
||||
------------
|
||||
|
||||
To create the quantity object from a :term:`scalar` we just have to pass
|
||||
the value to the `quantity` class template explicit constructor::
|
||||
|
||||
quantity<si::dim_length, si::kilometre, double> d(123);
|
||||
|
||||
.. note::
|
||||
|
||||
As the constructor is explicit, the quantity object can be created from
|
||||
an "unsafe" fundamental type only via
|
||||
`direct initialization <https://en.cppreference.com/w/cpp/language/direct_initialization>`_.
|
||||
This is why the code below using
|
||||
`copy initialization <https://en.cppreference.com/w/cpp/language/copy_initialization>`_
|
||||
**does not compile**::
|
||||
|
||||
quantity<si::dim_length, si::kilometre, double> d = 123; // ERROR
|
||||
|
||||
To simplify `quantity` objects creation the library provides helper aliases for
|
||||
quantities of each :term:`dimension` which additionally set the representation
|
||||
type to ``double`` by default::
|
||||
|
||||
namespace si {
|
||||
|
||||
template<Unit U, Scalar Rep = double>
|
||||
using length = quantity<dim_length, U, Rep>;
|
||||
|
||||
}
|
||||
|
||||
Thanks to that, the above example can be rewritten as follows::
|
||||
|
||||
si::length<si::kilometre> d(123);
|
||||
|
||||
To further simplify construction of quantities with compile-time known
|
||||
values the library provides :abbr:`UDL (User Defined Literal)` s for each
|
||||
:term:`unit` of every :term:`dimension`. Thanks to them the same code can
|
||||
be as simple as::
|
||||
|
||||
using namespace si::literals;
|
||||
constexpr auto d1 = 123q_km; // si::length<si::kilometre, std::int64_t>
|
||||
constexpr auto d2 = 123.q_km; // si::length<si::kilometre, long double>
|
||||
|
||||
``123q_km`` should be read as a quantity of length in kilometers. Initially the
|
||||
library did not use the ``q_`` prefix for UDLs but it turned out that there are
|
||||
a few unit symbols that collide with literals already existing in C and C++
|
||||
language (i.e. ``F`` (farad), ``J`` (joule), ``W`` (watt), ``K`` (kelvin),
|
||||
``d`` (day), ``l`` or ``L`` (litre), ``erg``, ``ergps``). This is why the
|
||||
``q_`` prefix was consistently applied to all the UDLs.
|
||||
|
||||
|
||||
Dimension-specific concepts
|
||||
---------------------------
|
||||
|
||||
In case the user does not care about the specific unit and representation but
|
||||
requires quantity of a concrete dimension than dimension-specific concepts can
|
||||
be used::
|
||||
|
||||
using namespace si::literals;
|
||||
constexpr Length auto d = 123q_km; // si::length<si::kilometre, std::int64_t>
|
||||
|
||||
.. note::
|
||||
|
||||
All instances of `quantity` class always match the `Quantity` concept.
|
||||
All other regular types that are not quantities are called
|
||||
:term:`scalars <scalar>` by the library and match the `Scalar` concept.
|
||||
|
||||
However, the above is not the most important usage of those concepts. Let's
|
||||
assume that the user wants to implement an ``avg_speed`` function that will
|
||||
be calculating the average speed based on provided distance and duration
|
||||
quantities. The usage of such a function can look as follows::
|
||||
|
||||
using namespace si::literals;
|
||||
using namespace international::literals;
|
||||
constexpr Velocity auto v1 = avg_speed(220q_km, 2q_h);
|
||||
constexpr Velocity auto v2 = avg_speed(140q_mi, 2q_h);
|
||||
|
||||
In this and all other physical units libraries such a function can be
|
||||
implemented as::
|
||||
|
||||
constexpr si::velocity<si::metre_per_second> avg_speed(si::length<si::metre> d,
|
||||
si::time<si::second> t)
|
||||
{
|
||||
return d / t;
|
||||
}
|
||||
|
||||
While being correct, this function performs unnecessary intermediate
|
||||
conversions (from kilometers to meters, from hours to seconds,
|
||||
and from meters per second to kilometers per hour) which can affect
|
||||
runtime performance and the precision of the final result. To eliminate
|
||||
all that overhead we have to write a template function::
|
||||
|
||||
template<typename U1, typename R1, typename U2, typename R2>
|
||||
constexpr auto avg_speed(si::length<U1, R1> d, si::time<U2, R2> t)
|
||||
{
|
||||
return d / t;
|
||||
}
|
||||
|
||||
This function will work for every SI unit and representation without any
|
||||
unnecessary overhead. It is also simple enough to prove its implementation
|
||||
being correct just by a simple inspection. However, it might not always be
|
||||
the case. For more complicated calculations we would like to ensure that we
|
||||
are returning a physical quantity of a correct dimension. For this
|
||||
dimension-specific concepts come handy again and with usage of C++20 generic
|
||||
functions our function can look as simple as::
|
||||
|
||||
constexpr Velocity auto avg_speed(Length auto d, Time auto t)
|
||||
{
|
||||
return d / t;
|
||||
}
|
||||
|
||||
Now we are sure that the dimension of returned quantity is correct. Also
|
||||
please note that with the above code we implemented a truly generic function
|
||||
that works efficiently not only with SI units but also with other systems of
|
||||
units like CGS.
|
||||
|
||||
.. seealso::
|
||||
|
||||
Please refer to :ref:`avg_speed` example for more information on different
|
||||
kinds of interfaces supported by the library.
|
||||
|
||||
|
||||
Working with constrained deduced quantity types
|
||||
-----------------------------------------------
|
||||
|
||||
It is important to note that when we assign a result from the function to an
|
||||
automatically deduced type, even if it is constrained by a dimension-specific
|
||||
concept, we still do not know what is the exact unit and representation type
|
||||
of such a quantity. In many cases it might be exactly what we want to get,
|
||||
but often we would like to know a specific type too. We have two options here:
|
||||
|
||||
- query the actual dimension, unit, and representation types::
|
||||
|
||||
constexpr Velocity auto v = avg_speed(220q_km, 2q_h);
|
||||
using quantity_type = decltype(v);
|
||||
using dimension_type = quantity_type::dimension;
|
||||
using unit_type = quantity_type::unit;
|
||||
using rep_type = quantity_type::rep;
|
||||
|
||||
- convert or cast to a desired quantity type::
|
||||
|
||||
constexpr Velocity auto v1 = avg_speed(220.q_km, 2q_h);
|
||||
constexpr si::velocity<si::metre_per_second> v2 = v1;
|
||||
constexpr Velocity auto v3 = quantity_cast<si::velocity<si::metre_per_second>(v1);
|
||||
|
||||
.. seealso::
|
||||
|
||||
More information on this subject can be found in :ref:`Conversions and Casting`
|
||||
chapter.
|
||||
@@ -0,0 +1,45 @@
|
||||
.. namespace:: units
|
||||
|
||||
Text output
|
||||
===========
|
||||
|
||||
Beside providing dimensional analysis and units conversions, the library
|
||||
also tries really hard to print any quantity in the most user friendly way.
|
||||
|
||||
Output streams
|
||||
--------------
|
||||
|
||||
The easiest way to print a quantity is to provide its object to the output
|
||||
stream::
|
||||
|
||||
using namespace si::literals;
|
||||
using namespace international::literals;
|
||||
constexpr Velocity auto v1 = avg_speed(220.q_km, 2q_h);
|
||||
constexpr Velocity auto v2 = avg_speed(140.q_mi, 2q_h);
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << v2 << '\n'; // 70 mi/h
|
||||
|
||||
The text output will always print the :term:`value of a quantity` followed
|
||||
by the symbol of a :term:`unit` associated with this quantity. We will learn
|
||||
more about units in the :ref:`Units` chapter, but for now, it is important
|
||||
to remember that it is a good practice to always `quantity_cast()` a quantity
|
||||
of an unknown ``auto`` type before passing it to the text output::
|
||||
|
||||
std::cout << quantity_cast<si::kilometre_per_hour>(v1) << '\n'; // 110 km/h
|
||||
std::cout << quantity_cast<si::metre_per_second>(v1) << '\n'; // 30.5556 m/s
|
||||
|
||||
|
||||
Formatting the output
|
||||
---------------------
|
||||
|
||||
Grammar:
|
||||
|
||||
.. productionlist::
|
||||
units-format-spec: fill-and-align[opt] sign[opt] width[opt] precision[opt] units-specs[opt]
|
||||
units-specs: conversion-spec
|
||||
: units-specs conversion-spec
|
||||
: units-specs literal-char
|
||||
literal-char: any character other than '{' or '}'
|
||||
conversion-spec: '%' modifier[opt] type
|
||||
modifier: one of 'E', 'O'
|
||||
type: one of 'n', 'q', 'Q', 't', '%'
|
||||
@@ -0,0 +1,319 @@
|
||||
.. namespace:: units
|
||||
|
||||
Units
|
||||
=====
|
||||
|
||||
Each quantity has a magnitude (a numerical value). In order to be able to
|
||||
compare quantities of the same dimension a notion of a :term:`measurement unit`
|
||||
was introduced. Units are designated by conventionally assigned names and
|
||||
symbols. Thanks to them it is possible to compare two quantities of the
|
||||
same dimension and express the ratio of the second quantity to the first
|
||||
one as a number. For example ``10s`` is ``10`` times more than ``1s``.
|
||||
|
||||
Base quantities are expressed in terms of :term:`base units <base unit>`
|
||||
(i.e. ``m`` (meter), ``s`` (second)), while derived quantities are expressed
|
||||
in terms of :term:`derived units <derived unit>`.
|
||||
|
||||
|
||||
Base Units
|
||||
----------
|
||||
|
||||
:term:`Base units <base unit>` are the units of
|
||||
:term:`base quantities <base quantity>` defined for
|
||||
:term:`base dimensions <base dimension>`. For example in :term:`SI`
|
||||
``m`` (meter) is a base unit of length, ``s`` (second) is a base unit of
|
||||
time. In each :term:`coherent system of units`, there is only one base
|
||||
unit for each base quantity. This is why a base unit type is required by
|
||||
the `base_dimension` definition in this library. For example `si::dim_length`
|
||||
can be defined in the following way::
|
||||
|
||||
namespace si {
|
||||
|
||||
struct dim_length : base_dimension<"L", metre> {};
|
||||
|
||||
}
|
||||
|
||||
where `si::metre` is defined as::
|
||||
|
||||
namespace si {
|
||||
|
||||
struct metre : named_unit<metre, "m", prefix> {};
|
||||
|
||||
}
|
||||
|
||||
In the above definition ``"m"`` is the unit symbol to be used in the text
|
||||
output, and ``si::prefix`` specifies that the library should allow
|
||||
definitions of prefixed units using `si::metre` as a reference (i.e.
|
||||
`si::centimetre`).
|
||||
|
||||
.. seealso::
|
||||
|
||||
For more information on prefixes and scaling please refer to
|
||||
`Scaled Units`_.
|
||||
|
||||
.. note::
|
||||
|
||||
The first template argument of `named_unit` is the type of the
|
||||
child class inherited from the instantiation of this `named_unit`
|
||||
class template. This is called a
|
||||
:abbr:`CRTP (Curiously Recurring Template Parameter)` Idiom and is used
|
||||
in many places in this library to provide :ref:`The Downcasting Facility`.
|
||||
Hopefully if [P0847]_ will land in C++23 the additional CRTP-related
|
||||
template parameter will be removed from this definition.
|
||||
|
||||
|
||||
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
|
||||
`CGS <https://en.wikipedia.org/wiki/Centimetre%E2%80%93gram%E2%80%93second_system_of_units>`_
|
||||
could be defined as::
|
||||
|
||||
namespace cgs {
|
||||
|
||||
struct dim_length : base_dimension<"L", si::centimetre> {};
|
||||
|
||||
}
|
||||
|
||||
The fact that both base dimensions use the same identifier ``"L"`` tells
|
||||
the library that bot 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 `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).
|
||||
|
||||
|
||||
Derived Units
|
||||
-------------
|
||||
|
||||
Derived units can be either named or unnamed.
|
||||
|
||||
Derived Named Units
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Derived named units have a unique symbol (i.e. ``N`` (newton) or ``Pa``
|
||||
(pascal)) and they are defined in the same way as base units (which
|
||||
always have to be a named unit)::
|
||||
|
||||
namespace si {
|
||||
|
||||
struct newton : named_unit<newton, "N", prefix> {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
Derived Unnamed Units
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Derived unnamed units are the units where the symbol is derived from the
|
||||
base quantities symbols and the expression of the dependence of the derived
|
||||
quantity on the base quantities (i.e. ``m/s`` (metre per second), ``m²``
|
||||
(square metre)). To support such use cases a library introduced a notion of
|
||||
:term:`derived dimension recipe` which stores the information about the
|
||||
order, exponents, and types of dimensions used to defined this particular
|
||||
derived dimension. For example each of the below ``momentum`` definitions
|
||||
will result in a different unnamed unit symbol:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 2-4, 6-8, 10-12
|
||||
|
||||
struct dim_momentum : derived_dimension<dim_momentum, kilogram_metre_per_second,
|
||||
exp<si::dim_mass, 1>,
|
||||
exp<si::dim_length, 1>,
|
||||
exp<si::dim_time, -1>> {}; // kg⋅m/s
|
||||
struct dim_momentum : derived_dimension<dim_momentum, kilogram_metre_per_second,
|
||||
exp<si::dim_length, 1>,
|
||||
exp<si::dim_mass, 1>,
|
||||
exp<si::dim_time, -1>> {}; // m⋅kg/s
|
||||
struct dim_momentum : derived_dimension<dim_momentum, kilogram_metre_per_second,
|
||||
exp<si::dim_time, -1>,
|
||||
exp<si::dim_length, 1>,
|
||||
exp<si::dim_mass, 1>> {}; // 1/s⋅m⋅kg
|
||||
|
||||
where ``kilogram_metre_per_second`` is defined as::
|
||||
|
||||
struct kilogram_metre_per_second : unit<kilogram_metre_per_second> {};
|
||||
|
||||
However, the easiest way to define momentum is just to use the
|
||||
`si::velocity` derived dimension in the recipe:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 3
|
||||
|
||||
struct dim_momentum : derived_dimension<dim_momentum, kilogram_metre_per_second,
|
||||
exp<si::dim_mass, 1>,
|
||||
exp<si::dim_velocity, 1>> {}; // kg⋅m/s
|
||||
|
||||
In such a case the library will do its magic and will automatically
|
||||
unpack a provided derived dimension to its base dimensions in order to
|
||||
end up with a :term:`normalized derived dimension` for a parent entity.
|
||||
|
||||
|
||||
The need to support a derived dimension in the recipe is not just a
|
||||
syntactic sugar that allows us to do less typing. It is worth to notice
|
||||
here that some of the derived unnamed units are defined in terms of other
|
||||
derived named units (i.e. surface tension quantity is measured in terms
|
||||
of ``N/m``):
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 2
|
||||
|
||||
struct dim_surface_tension : derived_dimension<dim_surface_tension, newton_per_metre,
|
||||
exp<si::dim_force, 1>,
|
||||
exp<si::dim_length, -1>> {}; // N/m
|
||||
|
||||
If we defined the above in terms of base units we would end up with
|
||||
a ``kg/s²`` derived unit symbol.
|
||||
|
||||
|
||||
Scaled Units
|
||||
------------
|
||||
|
||||
Until now we talked mostly about
|
||||
:term:`coherent units <coherent derived unit>` which are units used to
|
||||
define dimensions and thus, in their system of units, have proportionality
|
||||
factor/ratio equals one. However quantities of each dimension can also use
|
||||
other units of measurement to describe their magnitude (numerical value).
|
||||
|
||||
|
||||
Scaled Units
|
||||
^^^^^^^^^^^^
|
||||
|
||||
We are used to use minutes, hours, or days to measure quantities of time.
|
||||
Those units are the scaled versions of a time dimension's base unit,
|
||||
namely second. Those can be defined easily in the library using
|
||||
`named_scaled_unit` class template::
|
||||
|
||||
struct minute : named_scaled_unit<minute, "min", no_prefix, ratio<60>, second> {};
|
||||
struct hour : named_scaled_unit<hour, "h", no_prefix, ratio<60>, minute> {};
|
||||
struct day : named_scaled_unit<hour, "d", no_prefix, ratio<24>, hour> {};
|
||||
|
||||
where `no_prefix` is a special tag type describing that the library should
|
||||
not allow to define a new prefixed unit that would use this unit as a
|
||||
reference ("kilohours" does not have much sense, right?). The `ratio` type
|
||||
used in the definition is really similar to ``std::ratio`` but it takes
|
||||
the third additional argument that defines the exponent of the ratio.
|
||||
Thanks to it we can address nearly infinite scaling factors between units
|
||||
and define units like:
|
||||
|
||||
.. code-block::
|
||||
:force:
|
||||
|
||||
struct electronvolt : named_scaled_unit<electronvolt, "eV", prefix,
|
||||
ratio<1'602'176'634, 1'000'000'000, -19>, joule> {};
|
||||
|
||||
..
|
||||
TODO Submit a bug for above lexing problem
|
||||
|
||||
Finally, the last of the `named_scaled_unit` class template parameters
|
||||
provide a reference unit for scaling. Please note that it can be a dimension's
|
||||
base/coherent unit (like `si::second`) or any other unit (i.e. `si::minute`,
|
||||
`si::hour`) that is a scaled version of the dimension's base/coherent unit.
|
||||
|
||||
|
||||
Prefixed Unit
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Prefixed units are just scaled units with a standardized ratio. For example
|
||||
:term:`SI` defines prefixes based on the exponent of ``10``. Here is a
|
||||
complete list of all the :term:`SI` prefixes supported by the library::
|
||||
|
||||
namespace si {
|
||||
|
||||
struct prefix : prefix_type {};
|
||||
|
||||
struct yocto : units::prefix<yocto, prefix, "y", ratio<1, 1, -24>> {};
|
||||
struct zepto : units::prefix<zepto, prefix, "z", ratio<1, 1, -21>> {};
|
||||
struct atto : units::prefix<atto, prefix, "a", ratio<1, 1, -18>> {};
|
||||
struct femto : units::prefix<femto, prefix, "f", ratio<1, 1, -15>> {};
|
||||
struct pico : units::prefix<pico, prefix, "p", ratio<1, 1, -12>> {};
|
||||
struct nano : units::prefix<nano, prefix, "n", ratio<1, 1, -9>> {};
|
||||
struct micro : units::prefix<micro, prefix, "µ", ratio<1, 1, -6>> {};
|
||||
struct milli : units::prefix<milli, prefix, "m", ratio<1, 1, -3>> {};
|
||||
struct centi : units::prefix<centi, prefix, "c", ratio<1, 1, -2>> {};
|
||||
struct deci : units::prefix<deci, prefix, "d", ratio<1, 1, -1>> {};
|
||||
struct deca : units::prefix<deca, prefix, "da", ratio<1, 1, 1>> {};
|
||||
struct hecto : units::prefix<hecto, prefix, "h", ratio<1, 1, 2>> {};
|
||||
struct kilo : units::prefix<kilo, prefix, "k", ratio<1, 1, 3>> {};
|
||||
struct mega : units::prefix<mega, prefix, "M", ratio<1, 1, 6>> {};
|
||||
struct giga : units::prefix<giga, prefix, "G", ratio<1, 1, 9>> {};
|
||||
struct tera : units::prefix<tera, prefix, "T", ratio<1, 1, 12>> {};
|
||||
struct peta : units::prefix<peta, prefix, "P", ratio<1, 1, 15>> {};
|
||||
struct exa : units::prefix<exa, prefix, "E", ratio<1, 1, 18>> {};
|
||||
struct zetta : units::prefix<zetta, prefix, "Z", ratio<1, 1, 21>> {};
|
||||
struct yotta : units::prefix<yotta, prefix, "Y", ratio<1, 1, 24>> {};
|
||||
|
||||
}
|
||||
|
||||
Alternative hierarchy of prefixes is the one used in data information
|
||||
domain:
|
||||
|
||||
.. code-block::
|
||||
:force:
|
||||
|
||||
namespace data {
|
||||
|
||||
struct prefix : prefix_type {};
|
||||
|
||||
struct kibi : units::prefix<kibi, prefix, "Ki", ratio< 1'024>> {};
|
||||
struct mebi : units::prefix<mebi, prefix, "Mi", ratio< 1'048'576>> {};
|
||||
struct gibi : units::prefix<gibi, prefix, "Gi", ratio< 1'073'741'824>> {};
|
||||
struct tebi : units::prefix<tebi, prefix, "Ti", ratio< 1'099'511'627'776>> {};
|
||||
struct pebi : units::prefix<pebi, prefix, "Pi", ratio< 1'125'899'906'842'624>> {};
|
||||
struct exbi : units::prefix<exbi, prefix, "Ei", ratio<1'152'921'504'606'846'976>> {};
|
||||
|
||||
}
|
||||
|
||||
With the definitions like above we can easily define prefixed unit. For
|
||||
example we can define `si::kilometre` as::
|
||||
|
||||
namespace si {
|
||||
|
||||
struct kilometre : prefixed_unit<kilometre, kilo, metre> {};
|
||||
|
||||
}
|
||||
|
||||
.. important::
|
||||
|
||||
Prefixed units have to use named units as a reference. For unnamed
|
||||
units we could end up with some strange, misleading, and sometimes
|
||||
wrong definitions ("kilo square metre" seams strange and spelled
|
||||
as ``km²`` would be invalid).
|
||||
|
||||
|
||||
Deduced Units
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
For some units determining of a correct scaling ratio may not be trivial,
|
||||
and even if done correctly, may be a pain to maintain. For a simple example
|
||||
let's take a "kilometre per hour" unit. What is the easiest to maintain
|
||||
ratio in reference to "metre per second":
|
||||
|
||||
- ``1000/3600``
|
||||
- ``10/36``
|
||||
- ``5/18``
|
||||
|
||||
Whichever, we choose there will always be someone not happy with our choice.
|
||||
|
||||
Thanks to a `deduced_unit` class template provided by the library this problem
|
||||
does not exist at all. With it `si::kilometre_per_hour` can be defined as::
|
||||
|
||||
namespace si {
|
||||
|
||||
struct kilometre_per_hour : deduced_unit<kilometre_per_hour, dim_velocity, kilometre, hour> {};
|
||||
|
||||
}
|
||||
|
||||
Please note that this is the only unit-related class template that takes
|
||||
a dimension as its parameter. This derived dimension provides a :term:`recipe`
|
||||
used for its definition. Based on the information stored in the recipe
|
||||
(order, type, and exponents of composite dimensions) and the ratios of units
|
||||
provided in the template parameter list after the derived dimension parameter,
|
||||
the library calculates the final ratio for this unit.
|
||||
|
||||
|
||||
.. rubric:: Citations:
|
||||
|
||||
.. [P0847] `"Deducing this" <https://wg21.link/P0847>`_, Programming Language C++ proposal
|
||||
Reference in New Issue
Block a user