From 3a94ca89fd8b9e2f36e9c82d2a1823570ce6efed Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 8 Jul 2023 12:59:38 +0200 Subject: [PATCH] feat(example): `spectroscopy_units` example added --- .../character_of_a_quantity.md | 158 ++++++++++++++++++ .../quantities_of_different_characters.md | 0 example/CMakeLists.txt | 1 + example/spectroscopy_units.cpp | 104 ++++++++++++ mkdocs.yml | 2 +- 5 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 docs/users_guide/framework_basics/character_of_a_quantity.md delete mode 100644 docs/users_guide/framework_basics/quantities_of_different_characters.md create mode 100644 example/spectroscopy_units.cpp diff --git a/docs/users_guide/framework_basics/character_of_a_quantity.md b/docs/users_guide/framework_basics/character_of_a_quantity.md new file mode 100644 index 00000000..721317e1 --- /dev/null +++ b/docs/users_guide/framework_basics/character_of_a_quantity.md @@ -0,0 +1,158 @@ +# Character of a Quantity + +!!! warning + + Features described in this chapter are experimental and are subject to change or removal. Please + share your feedback if something doesn't seem right or could be improved. + + +## Scalars, vectors, and tensors + +!!! quote "ISO 80000-2" + + Scalars, vectors and tensors are mathematical objects that can be used to denote certain physical + quantities and their values. They are as such independent of the particular choice of a coordinate system, + whereas each scalar component of a vector or a tensor and each component vector and component + tensor depend on that choice. + +While defining quantities ISO 80000 explicitly mentions when a specific quantity has a vector or tensor +character. Such distinction is important because each quantity character represent different properties +and allows different operations to be done on their quantities. + +For example, imagine a physical units library that allows to create a `speed` quantity from both +`length / time` and `length * time`. It wouldn't be too safe to use such a product, right? + +Now we have to realize that both of the above operations (multiplication and division) are even not +mathematically defined for linear algebra types such as vector or tensors. On the other hand, two vectors +can be passed as arguments to dot (`⋅`) and cross (`×`) product operations. The result of the first one is +a scalar. The second one results with a vector that is perpendicular to both vectors passed as arguments. +Again, it wouldn't be safe to allow replacing those two operations with each other or expect the same +results from both cases. This simply can't work. + + +## Examples from the ISQ + +To provide some examples for further discussion let's pick a few quantities defined in the ISO 80000: + +| Quantity | Character | Quantity Equation | +|------------------------|:------------:|:-------------------------------------------------:| +| `duration` | scalar | _{base quantity}_ | +| `mass` | scalar | _{base quantity}_ | +| `length` | scalar | _{base quantity}_ | +| `path_length` | scalar | _{base quantity}_ | +| `radius` | scalar | _{base quantity}_ | +| `position_vector` | **vector** | _{base quantity}_ | +| `velocity` | **vector** | `position_vector / duration` | +| `acceleration` | **vector** | `velocity / duration` | +| `force` | **vector** | `mass * acceleration` | +| `power` | scalar | `force ⋅ velocity` | +| `moment_of_force` | **vector** | `position_vector × force` | +| `torque` | scalar | `moment_of_force ⋅ {unit-vector}` | +| `surface_tension` | scalar | `|force| / length` | +| `angular_displacement` | scalar | `path_length / radius` | +| `angular_velocity` | **vector** | `angular_displacement / duration * {unit-vector}` | +| `momentum` | **vector** | `mass * velocity` | +| `angular_momentum` | **vector** | `position_vector × momentum` | +| `moment_of_inertia` | **_tensor_** | `angular_momentum ⊗ angular_velocity` | + +!!! note + + As of now, all of the C++ physical units libraries on the market besides **mp-units** do not + support above-mentioned operations. They expose only multiplication and division operators which + do not work for proper linear-algebra-based representation types. In case one would like to + construct the quantities provided in the above table with those libraries, this would result with + a compile-time error stating that multiplication and division of two linear-algebra vectors is not + possible. + + +## Characters apply to quantities but not dimensions or units + +ISO 80000 explicitly states that dimensions are orthogonal to quantity characters: + +!!! quote "ISO 80000-1:2009" + + In deriving the dimension of a quantity, no account is taken of its scalar, vector, or tensor character. + +Also, it explicitly states that: + +!!! quote "ISO 80000-2" + + All units are scalars. + +## Defining vector and tensor quantities + +To specify that a specific quantity has a vector or tensor character a value of `quantity_character` +enumeration can be appended to the `quantity_spec` describing such a quantity type: + +```cpp +inline constexpr struct position_vector : quantity_spec {} position_vector; +inline constexpr struct displacement : quantity_spec {} displacement; +``` + +From now on all the quantities derived from `position_vector` or `displacement` will have a correct +character consistent with the operations performed in the [quantity equation](../../../appendix/glossary/#quantity-equation) +on their arguments. + + +## Representation types for vector and tensor quantities + +!!! note + + The current version of the C++ Standard Library does not provide any types that could be used as + a representation type for vector and tensor quantities. This is why users are on their own here + :worried:. + + To provide examples and implement unit tests our library uses the types proposed in the [P1385](https://wg21.link/p1385) + and available as [a Conan package in the Conan Center](https://conan.io/center/wg21-linear_algebra). + +In order to enable the usage of a user-defined type as a representation type for vector or tensor +quantities you need to provide a partial specialization of `is_vector` or `is_tensor` customization +points. + +For example, here is how it can be done for the [P1385](https://wg21.link/p1385) types: + +```cpp +#include + +using la_vector = STD_LA::fixed_size_column_vector; + +template<> +inline constexpr bool mp_units::is_vector = true; +``` + + +## Hacking the character + +Sometimes you want to use a vector quantity but you do not care about its direction. For example, +the standard gravity acceleration constant is always pointing down and you might not care about this +in a particular case. In such a case you may want to "hack" the library to allow scalar types +to be used as a representation type for scalar quantities. + +For example, you can do something like this: + +```cpp +template + requires mp_units::is_scalar +inline constexpr bool mp_units::is_vector = true; +``` + +which says that every type that can be used a scalar representation is also allowed for vector +quantities. + +Doing the above is actually not such a big "hack" as the ISO 80000 explicitly allows it: + + +!!! quote "ISO 80000-2" + + A vector is a tensor of the first order and a scalar is a tensor of order zero. + +However, for type-safety reasons, we do not want to allow of such a behavior by default. + + +## Different shades of vector and tensor quantities + + + + Instead of treating each coordinate of a vector as a physical quantity value (i.e. a number multiplied by + a unit), the vector could be written as a numerical vector multiplied by a unit. + The same considerations apply to tensors of second and higher orders. diff --git a/docs/users_guide/framework_basics/quantities_of_different_characters.md b/docs/users_guide/framework_basics/quantities_of_different_characters.md deleted file mode 100644 index e69de29b..00000000 diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index a06b518a..92bc123a 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -53,6 +53,7 @@ add_example(glide_computer mp-units::core-fmt mp-units::international mp-units:: add_example(hello_units mp-units::core-fmt mp-units::core-io mp-units::si mp-units::usc) add_example(measurement mp-units::core-io mp-units::si) add_example(si_constants mp-units::core-fmt mp-units::si) +add_example(spectroscopy_units mp-units::core-fmt mp-units::si) add_example(storage_tank mp-units::core-fmt mp-units::si mp-units::utility) add_example( strong_angular_quantities mp-units::core-fmt mp-units::core-io mp-units::si mp-units::isq_angle mp-units::utility diff --git a/example/spectroscopy_units.cpp b/example/spectroscopy_units.cpp new file mode 100644 index 00000000..64a6f18d --- /dev/null +++ b/example/spectroscopy_units.cpp @@ -0,0 +1,104 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include + +// This example implements a table of units provided in the following article +// http://cds.cern.ch/record/1481609/files/978-3-642-18018-7_BookBackMatter.pdf + +using namespace mp_units; + +// import selected unit symbols only +using mp_units::si::unit_symbols::cm; +using mp_units::si::unit_symbols::eV; +using mp_units::si::unit_symbols::K; +using mp_units::si::unit_symbols::kJ; +using mp_units::si::unit_symbols::mol; +using mp_units::si::unit_symbols::THz; +using mp_units::si::unit_symbols::um; + +// physical constants +constexpr auto c = 1 * si::si2019::speed_of_light_in_vacuum; +constexpr auto h = 1 * si::si2019::planck_constant; +constexpr auto kb = 1 * si::si2019::boltzmann_constant; + +// prints quantities in the resulting unit +template T1, QuantityOf T2, QuantityOf T3, + QuantityOf T4, QuantityOf T5> +void print_line(const std::tuple& t) +{ + MP_UNITS_STD_FMT::println("| {:<15} | {:<15} | {:<15} | {:<15} | {:<15} |", std::get<0>(t), std::get<1>(t), + std::get<2>(t), std::get<3>(t), std::get<4>(t)); +} + +// prints quantities in semi-SI units +// (eV is not an official SI unit) +template T1, QuantityOf T2, QuantityOf T3, + QuantityOf T4, QuantityOf T5> +void print_line_si(const std::tuple& t) +{ + MP_UNITS_STD_FMT::println("| {:<15} | {:<15} | {:<15} | {:<15} | {:<15} |", std::get<0>(t)[eV], + std::get<1>(t)[1 / cm], std::get<2>(t)[THz], std::get<3>(t)[K], std::get<4>(t)[um]); +} + +int main() +{ + const auto q1 = isq::energy(1. * eV); + const auto t1 = std::make_tuple(q1, isq::wavenumber(q1 / (h * c)), isq::frequency(q1 / h), + isq::thermodynamic_temperature(q1 / kb), isq::wavelength(h * c / q1)); + + const auto q2 = 1. * isq::wavenumber[1 / cm]; + const auto t2 = std::make_tuple(isq::energy(q2 * h * c), q2, isq::frequency(q2 * c), + isq::thermodynamic_temperature(q2 * h * c / kb), isq::wavelength(1 / q2)); + + const auto q3 = isq::frequency(1. * THz); + const auto t3 = std::make_tuple(isq::energy(q3 * h), isq::wavenumber(q3 / c), q3, + isq::thermodynamic_temperature(q3 * h / kb), isq::wavelength(c / q3)); + + const auto q4 = isq::thermodynamic_temperature(1. * K); + const auto t4 = std::make_tuple(isq::energy(q4 * kb), isq::wavenumber(q4 * kb / (h * c)), isq::frequency(q4 * kb / h), + q4, isq::wavelength(h * c / (q4 * kb))); + + const auto q5 = isq::wavelength(1. * um); + const auto t5 = std::make_tuple(isq::energy(h * c / q5), isq::wavenumber(1 / q5), isq::frequency(c / q5), + isq::thermodynamic_temperature(h * c / (q5 * kb)), q5); + + MP_UNITS_STD_FMT::println("| {:<15} | {:<15} | {:<15} | {:<15} | {:<15} |", "Energy", "Wavenumber", "Frequency", + "Temperature", "Wavelength"); + MP_UNITS_STD_FMT::println("| {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} |", ""); + print_line(t1); + print_line(t2); + print_line(t3); + print_line(t4); + print_line(t5); + + MP_UNITS_STD_FMT::println("| {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} |", ""); + print_line_si(t1); + print_line_si(t2); + print_line_si(t3); + print_line_si(t4); + print_line_si(t5); +} diff --git a/mkdocs.yml b/mkdocs.yml index eb6a8145..f2e4b0cd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -111,8 +111,8 @@ nav: - Systems of Units: users_guide/framework_basics/systems_of_units.md - Simple and Typed Quantities: users_guide/framework_basics/simple_and_typed_quantities.md - Value Conversions: users_guide/framework_basics/value_conversions.md + - Character of a Quantity: users_guide/framework_basics/character_of_a_quantity.md - Quantity Arithmetics: users_guide/framework_basics/quantity_arithmetics.md - - Quantities of Different Characters: users_guide/framework_basics/quantities_of_different_characters.md - Faster-than-lightspeed Constants: users_guide/framework_basics/faster_than_lightspeed_constants.md - Dimensionless Quantities: users_guide/framework_basics/dimensionless_quantities.md - The Affine Space: users_guide/framework_basics/the_affine_space.md