Documentation updated
This commit is contained in:
1 parent
cf7dab42c3
commit
df6a0da9d3
11 files changed
+499
-996
No files matched your search
@@ -3,17 +3,203 @@
|
||||
Extending the Library
|
||||
=====================
|
||||
|
||||
The library was designed with a simple extensibility in mind. It is easy to add new units,
|
||||
dimensions, and prefixes. The systems of units are not closed (classes) but open (namespaces)
|
||||
and can be easily extended, or its content can be partially/fully imported to other systems.
|
||||
|
||||
|
||||
Custom Units
|
||||
------------
|
||||
|
||||
It might happen that the user would like to use a unit that is not predefined by the library
|
||||
or is predefined but the user would like to name it differently, assign a different symbol
|
||||
to existing unit, or make it a base unit for prefixes.
|
||||
|
||||
|
||||
Defining a New Unit
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
My working desk is of ``180 cm x 60 cm`` which gives an area of ``0.3 m²``. I would like to
|
||||
make it a unit of area for my project::
|
||||
|
||||
struct desk : named_scaled_unit<desk, "desk", no_prefix, ratio<3, 10>, si::square_metre> {};
|
||||
|
||||
With the above I can define a quantity with the area of ``2 desks``::
|
||||
|
||||
auto d1 = si::area<desk>(2);
|
||||
|
||||
In case I feel it is too verbose to type the above every time I can easily create a custom
|
||||
alias or an :abbr:`UDL (User Defined Literal)`::
|
||||
|
||||
// alias with fixed integral representation
|
||||
using desks = si::area<desk, std::int64_t>;
|
||||
|
||||
// UDLs
|
||||
constexpr auto operator"" _d(unsigned long long l) { return si::area<desk, std::int64_t>(l); }
|
||||
constexpr auto operator"" _d(long double l) { return si::area<desk, long double>(l); }
|
||||
|
||||
Right now I am fully set up for my project and can start my work of tracking the area taken
|
||||
by my desks::
|
||||
|
||||
auto d1 = si::area<desk>(2);
|
||||
auto d2 = desks(3);
|
||||
auto d3 = 1_d;
|
||||
auto sum = d1 + d2 + d3;
|
||||
std::cout << "Area: " << sum << '\n'; // prints 'Area: 6 desk'
|
||||
|
||||
In case I would like to check how much area ``6 desks`` take in SI units::
|
||||
|
||||
auto sum_si = quantity_cast<si::square_metre>(sum);
|
||||
std::cout << "Area (SI): " << sum_si << '\n'; // prints 'Area (SI): 1.8 m²'
|
||||
|
||||
|
||||
Enabling a Unit for Prefixing
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In case I decide it is reasonable to express my desks with SI prefixes the only thing I have
|
||||
to change in the above code is to replace `no_prefix` with `si_prefix`::
|
||||
|
||||
struct desk : named_scaled_unit<desk, "desk", si::prefix, ratio<3, 10>, si::square_metre> {};
|
||||
|
||||
Now I can define a new unit named ``kilodesk``::
|
||||
|
||||
struct kilodesk : prefixed_unit<kilodesk, si::kilo, desk> {};
|
||||
static_assert(3_d * 1000 == si::area<kilodesk>(3));
|
||||
|
||||
But maybe SI prefixes are not good for me. Maybe I always pack ``6`` desks into one package
|
||||
for shipment and ``40`` such packages fit into my lorry. To express this with prefixes a new
|
||||
prefix family and prefixes are needed::
|
||||
|
||||
struct shipping_prefix : prefix_family {};
|
||||
|
||||
struct package : prefix<package, shipping_prefix, "pkg", ratio<6>> {};
|
||||
struct lorry : prefix<lorry, shipping_prefix, "lorry", ratio<6 * 40>> {};
|
||||
|
||||
Now we can use it for our unit::
|
||||
|
||||
struct desk : named_scaled_unit<desk, "desk", shipping_prefix, ratio<3, 10>, si::square_metre> {};
|
||||
struct packagedesk : prefixed_unit<packagedesk, package, desk> {};
|
||||
struct lorrydesk : prefixed_unit<lorrydesk, lorry, desk> {};
|
||||
|
||||
With the above::
|
||||
|
||||
static_assert(6_d == si::area<packagedesk>(1));
|
||||
static_assert(240_d == si::area<lorrydesk>(1));
|
||||
std::cout << "Area: " << quantity_cast<packagedesk>(sum) << '\n'; // prints 'Area: 1 pkgdesk'
|
||||
|
||||
It is important to notice that with the definition of a custom prefix I did not loose SI
|
||||
units compatibility. If I want to calculate how much area I can cover with desks delivered
|
||||
by ``3 lorries`` I can do the following::
|
||||
|
||||
auto area = quantity_cast<si::square_metre>(si::area<lorrydesk>(3));
|
||||
std::cout << "Area: " << area << '\n'; // prints 'Area: 216 m²'
|
||||
|
||||
|
||||
Custom Dimensions
|
||||
-----------------
|
||||
|
||||
Custom Base Dimensions
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
There are cases were a custom unit is not enough and the user would like to define custom
|
||||
dimensions. The most common case is to define a new derived dimension from other dimensions
|
||||
already predefined in various systems. But in **mp-units** library it is also really easy to
|
||||
define a new base dimension for a custom units system.
|
||||
|
||||
Custom Derived Dimensions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In case I want to track how many desks I can produce over time or what is the consumption
|
||||
rate of wood during production I need to define a new derived dimension together with its
|
||||
coherent unit::
|
||||
|
||||
// coherent unit must apply to the system rules (in this case SI)
|
||||
struct square_metre_per_second : unit<square_metre_per_second> {};
|
||||
|
||||
// new derived dimensions
|
||||
struct dim_desk_rate : derived_dimension<dim_desk_rate, square_metre_per_second,
|
||||
exp<si::dim_area, 1>, exp<si::dim_time, -1>> {};
|
||||
|
||||
// our unit of interest for a new derived dimension
|
||||
struct desk_per_hour : deduced_unit<desk_per_hour, dim_desk_rate, desk, si::hour> {};
|
||||
|
||||
// a quantity of our dimension
|
||||
template<Unit U, Scalar Rep = double>
|
||||
using desk_rate = quantity<dim_desk_rate, U, Rep>;
|
||||
|
||||
// a concept matching the above quantity
|
||||
template<typename T>
|
||||
concept DeskRate = QuantityOf<T, dim_desk_rate>;
|
||||
|
||||
With the above we can now check what is the production rate::
|
||||
|
||||
DeskRate auto rate = quantity_cast<desk_per_hour>(3._d / 20q_min);
|
||||
std::cout << "Desk rate: " << rate << '\n'; // prints 'Desk rate: 9 desk/h'
|
||||
|
||||
and how much wood is being consumed over a unit of time::
|
||||
|
||||
auto wood_rate = quantity_cast<square_metre_per_second>(rate);
|
||||
std::cout << "Wood rate: " << wood_rate << '\n'; // prints 'Wood rate: 0.00075 m²/s'
|
||||
|
||||
|
||||
Custom Base Dimensions
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In case I want to monitor what is the average number of people sitting by one desk in
|
||||
a customer's office I would need a unit called ``person_per_desk`` of a new derived
|
||||
dimension. However, our library does not know what a ``person`` is. For this I need to
|
||||
define a new base dimension, its units, quantity helper, concept, and UDLs::
|
||||
|
||||
struct person : named_unit<person, "person", no_prefix> {};
|
||||
struct dim_people : base_dimension<"people", person> {};
|
||||
|
||||
template<Unit U, Scalar Rep = double>
|
||||
using people = quantity<dim_people, U, Rep>;
|
||||
|
||||
template<typename T>
|
||||
concept People = QuantityOf<T, dim_people>;
|
||||
|
||||
constexpr auto operator"" _p(unsigned long long l) { return people<person, std::int64_t>(l); }
|
||||
constexpr auto operator"" _p(long double l) { return people<person, long double>(l); }
|
||||
|
||||
|
||||
With the above we can now define a new derived dimension::
|
||||
|
||||
struct person_per_square_metre : unit<person_per_square_metre> {};
|
||||
struct dim_occupancy_rate : derived_dimension<dim_occupancy_rate, person_per_square_metre,
|
||||
exp<dim_people, 1>, exp<si::dim_area, -1>> {};
|
||||
|
||||
struct person_per_desk : deduced_unit<person_per_desk, dim_occupancy_rate, person, desk> {};
|
||||
|
||||
template<Unit U, Scalar Rep = double>
|
||||
using occupancy_rate = quantity<dim_occupancy_rate, U, Rep>;
|
||||
|
||||
template<typename T>
|
||||
concept OccupancyRate = QuantityOf<T, dim_occupancy_rate>;
|
||||
|
||||
Now we can play with our new feature::
|
||||
|
||||
People auto employees = 1450._p;
|
||||
auto office_desks = 967_d;
|
||||
OccupancyRate auto occupancy = employees / office_desks;
|
||||
|
||||
std::cout << "Occupancy: " << occupancy << '\n'; // prints 'Occupancy: 1.49948 person/desk'
|
||||
|
||||
|
||||
Custom Systems
|
||||
--------------
|
||||
|
||||
Being able to extend predefined systems is a mandatory feature of any physical
|
||||
units library. Fortunately, for **mp-units** there is nothing special to do here.
|
||||
|
||||
A system is defined in terms of its base dimensions. If you are using only SI
|
||||
base dimensions then you are in the boundaries of the SI system. If you are
|
||||
adding new base dimensions, like we did in the `Custom Base Dimensions`_
|
||||
chapter, you are defining a new system.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
.. seealso::
|
||||
|
||||
More information on extending the library can be found in the
|
||||
:ref:`Using Custom Representation Types` chapter.
|
||||
@@ -0,0 +1,109 @@
|
||||
.. namespace:: units
|
||||
|
||||
Working with Unknown Dimensions and Their Units
|
||||
===============================================
|
||||
|
||||
From time to time the user of this library will face an `unknown_dimension` and
|
||||
`unknown_coherent_unit` types. This chapters describes their purpose and usage in
|
||||
detail.
|
||||
|
||||
What is an unknown dimension?
|
||||
-----------------------------
|
||||
|
||||
As we learned in the :ref:`Dimensions` chapter, in most cases the result of multiplying
|
||||
or dividing two quantities of specific dimensions is a quantity of yet another dimension.
|
||||
|
||||
If such a resulting dimension is predefined by the user (and a proper header file with its
|
||||
definition is included in the current translation unit) :ref:`The Downcasting Facility`
|
||||
will determine its type. The same applies to the resulting unit. For example:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 3,7-9
|
||||
|
||||
#include <units/physical/si/length.h>
|
||||
#include <units/physical/si/time.h>
|
||||
#include <units/physical/si/speed.h>
|
||||
|
||||
using namespace units::physical::si;
|
||||
|
||||
constexpr auto result = 144q_km / 2q_h;
|
||||
static_assert(std::is_same_v<decltype(result)::dimension, dim_velocity>);
|
||||
static_assert(std::is_same_v<decltype(result)::unit, kilometre_per_hour>);
|
||||
|
||||
However, if the resulting dimension is not predefined by the user the library framework
|
||||
will create an instance of an `unknown_dimension`. The coherent unit of such an unknown
|
||||
dimension is an `unknown_coherent_unit`. Let's see what happens with our example when
|
||||
we forget to include a header file with the resulting dimension definition:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 3,9,11
|
||||
|
||||
#include <units/physical/si/length.h>
|
||||
#include <units/physical/si/time.h>
|
||||
// #include <units/physical/si/speed.h>
|
||||
|
||||
using namespace units::physical::si;
|
||||
|
||||
constexpr auto result = 144q_km / 2q_h;
|
||||
static_assert(std::is_same_v<decltype(result)::dimension,
|
||||
unknown_dimension<exp<dim_length, 1>, exp<dim_time, -1>>>);
|
||||
static_assert(std::is_same_v<decltype(result)::unit,
|
||||
scaled_unit<ratio<1, 36, 1>, unknown_coherent_unit>>);
|
||||
|
||||
|
||||
Operations On Unknown Dimensions And Their Units
|
||||
------------------------------------------------
|
||||
|
||||
For some cases we can eliminate the need to predefine a specific dimension and just use
|
||||
the `unknown_dimension` instead. Let's play with the previous example a bit::
|
||||
|
||||
static_assert(result.count() == 72);
|
||||
|
||||
As we can see the value stored in this quantity can be easily obtained and contains a
|
||||
correct result. However, if we try to print its value to the text output we will get::
|
||||
|
||||
std::cout << "Speed: " << result << '\n'; // prints 'Speed: 72 [1/36 × 10¹] m/s'
|
||||
|
||||
The output from above program should not be a surprise. It is an unknown dimensions with
|
||||
a scaled unknown coherent unit. The library can't know what is the symbol of such unit
|
||||
so it does its best and prints the unit in terms of units of base dimensions that formed
|
||||
this particular unknown derived dimension.
|
||||
|
||||
In case we would like to print the result in terms of base units we can simply do the
|
||||
following::
|
||||
|
||||
auto s = quantity_cast<unknown_coherent_unit>(result);
|
||||
std::cout << "Speed: " << s << '\n'; // prints 'Speed: 20 m/s'
|
||||
|
||||
.. seealso::
|
||||
|
||||
Another good example of unknown dimension usage can be found in the
|
||||
:ref:`box_example`::
|
||||
|
||||
std::cout << "float rise rate = " << box.fill_level(measured_mass) / fill_time << '\n';
|
||||
|
||||
|
||||
Temporary Results
|
||||
-----------------
|
||||
|
||||
In many cases there is nothing inherently wrong with having unknown dimensions and units
|
||||
in your program. A typical example here are temporary results of a long calculation:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 5,7
|
||||
|
||||
auto some_long_calculation(Length auto d, Time auto t)
|
||||
{
|
||||
Speed auto s1 = avg_speed(d, t);
|
||||
|
||||
auto temp1 = s1 * 200q_km; // intermediate unknown dimension
|
||||
|
||||
Speed auto s2 = temp1 / 50q_km; // back to known dimensions again
|
||||
Length auto d2 = s2 * 4q_h;
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
If a programmer wants to break the calculation to several lines/variables he/she does not
|
||||
have to ensure that the intermediate results are of predefined dimensions or just a clear
|
||||
science fiction :-) The final result will always be correct.
|
||||
@@ -1,9 +0,0 @@
|
||||
.. namespace:: units
|
||||
|
||||
Working with Unknown Units and Dimensions
|
||||
=========================================
|
||||
|
||||
- what is an unknown unit?
|
||||
- what is an unknown dimension?
|
||||
- temporary result
|
||||
- casting to the coherent unit
|
||||
Reference in new issue
Block a user