feat: Initial Vector and Tensor representation concepts implementation

This commit is contained in:
Mateusz Pusz
2023-09-22 19:29:29 +02:00
parent dde5bcab7e
commit 31f7a43cc6
8 changed files with 327 additions and 120 deletions

View File

@ -77,6 +77,7 @@ concept ReferenceOf =
(QuantitySpec<std::remove_const_t<decltype(V)>> && implicitly_convertible(get_quantity_spec(T{}), V) && (QuantitySpec<std::remove_const_t<decltype(V)>> && implicitly_convertible(get_quantity_spec(T{}), V) &&
!detail::DerivedFromQuantityKindSpecOf<get_quantity_spec(T{}), V> && !detail::DerivedFromQuantityKindSpecOf<get_quantity_spec(T{}), V> &&
(detail::QuantityKindSpec<std::remove_const_t<decltype(get_quantity_spec(T{}))>> || (detail::QuantityKindSpec<std::remove_const_t<decltype(get_quantity_spec(T{}))>> ||
!detail::DerivedFromQuantityKindSpecOf<V, get_quantity_spec(T{})>))); !detail::DerivedFromQuantityKindSpecOf<V, get_quantity_spec(T{})>)) ||
(std::same_as<std::remove_const_t<decltype(V)>, quantity_character> && (get_quantity_spec(T{}).character == V)));
} // namespace mp_units } // namespace mp_units

View File

@ -52,35 +52,88 @@ enum class quantity_character { scalar, vector, tensor };
namespace detail { namespace detail {
template<typename T, typename U> template<typename T>
concept CommonTypeWith = concept Scalar = is_scalar<T>;
std::same_as<std::common_type_t<T, U>, std::common_type_t<U, T>> &&
std::constructible_from<std::common_type_t<T, U>, T> && std::constructible_from<std::common_type_t<T, U>, U>;
template<typename T, typename U = T>
concept ScalableNumber =
std::regular_invocable<std::multiplies<>, T, U> && std::regular_invocable<std::divides<>, T, U>;
template<typename T> template<typename T>
concept CastableNumber = CommonTypeWith<T, std::intmax_t> && ScalableNumber<std::common_type_t<T, std::intmax_t>>; concept Vector = is_vector<T>;
// TODO Fix it according to sudo_cast implementation
template<typename T> template<typename T>
concept Scalable = concept Tensor = is_tensor<T>;
CastableNumber<T> ||
(requires { typename T::value_type; } && CastableNumber<typename T::value_type> && template<typename T, quantity_character Ch>
ScalableNumber<T, std::common_type_t<typename T::value_type, std::intmax_t>>) || concept IsOfCharacter =
(requires { typename T::element_type; } && CastableNumber<std::remove_reference_t<typename T::element_type>> && (Ch == quantity_character::scalar && is_scalar<T>) || (Ch == quantity_character::vector && is_vector<T>) ||
ScalableNumber<T, std::common_type_t<std::remove_reference_t<typename T::element_type>, std::intmax_t>>); (Ch == quantity_character::tensor && is_tensor<T>);
template<typename T>
[[nodiscard]] consteval quantity_character get_character(T)
{
if constexpr (Tensor<T>)
return quantity_character::tensor;
else if constexpr (Vector<T>)
return quantity_character::vector;
else if constexpr (Scalar<T>)
return quantity_character::scalar;
}
// clang-format off
template<typename T>
concept ScalarRepresentation = std::regular<T> && Scalar<T> &&
requires(T a, T b, std::intmax_t i, long double f) {
{ a * i } -> Scalar;
{ i * a } -> Scalar;
{ a / i } -> Scalar;
{ a * f } -> Scalar; // TODO How this affects freestanding?
{ f * a } -> Scalar;
{ a / f } -> Scalar;
{ a + b } -> Scalar;
{ a - b } -> Scalar;
{ a * b } -> Scalar;
{ a / b } -> Scalar;
};
template<typename T>
concept VectorRepresentation = std::regular<T> && Vector<T> &&
requires(T a, T b, std::intmax_t i, long double f) {
{ a * i } -> Vector;
{ i * a } -> Vector;
{ a / i } -> Vector;
{ a * f } -> Vector;
{ f * a } -> Vector;
{ a / f } -> Vector;
{ a + b } -> Vector;
{ a - b } -> Vector;
{ dot_product(a, b) } -> Scalar;
{ cross_product(a, b) } -> Vector;
{ tensor_product(a, b) } -> Tensor;
{ norm(a) } -> Scalar;
};
template<typename T>
concept TensorRepresentation = std::regular<T> && Tensor<T> &&
requires(T a, T b, std::intmax_t i, long double f) {
{ a * i } -> Tensor;
{ i * a } -> Tensor;
{ a / i } -> Tensor;
{ a * f } -> Tensor;
{ f * a } -> Tensor;
{ a / f } -> Tensor;
{ a + b } -> Tensor;
{ a - b } -> Tensor;
{ tensor_product(a, b) } -> Tensor;
{ inner_product(a, b) } -> Tensor;
{ scalar_product(a, b) } -> Scalar;
};
// clang-format on
} // namespace detail } // namespace detail
template<typename T> template<typename T>
concept Representation = (is_scalar<T> || is_vector<T> || is_tensor<T>)&&std::regular<T> && detail::Scalable<T>; concept Representation =
detail::ScalarRepresentation<T> || detail::VectorRepresentation<T> || detail::TensorRepresentation<T>;
template<typename T, quantity_character Ch> template<typename T, quantity_character Ch>
concept RepresentationOf = Representation<T> && ((Ch == quantity_character::scalar && is_scalar<T>) || concept RepresentationOf = detail::IsOfCharacter<T, Ch> && Representation<T>;
(Ch == quantity_character::vector && is_vector<T>) ||
(Ch == quantity_character::tensor && is_tensor<T>));
} // namespace mp_units } // namespace mp_units

View File

@ -65,20 +65,6 @@ concept QuantityConvertibleTo =
// deduced thus the function is evaluated here and may emit truncating conversion or other warnings) // deduced thus the function is evaluated here and may emit truncating conversion or other warnings)
requires(QFrom q) { detail::sudo_cast<QTo>(q); }; requires(QFrom q) { detail::sudo_cast<QTo>(q); };
template<quantity_character Ch, typename Func, typename T, typename U>
concept InvokeResultOf = std::regular_invocable<Func, T, U> && RepresentationOf<std::invoke_result_t<Func, T, U>, Ch>;
template<typename Func, typename Q1, typename Q2>
concept InvocableQuantities = Quantity<Q1> && Quantity<Q2> &&
InvokeResultOf<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, Func,
typename Q1::rep, typename Q2::rep> &&
requires { common_reference(Q1::reference, Q2::reference); };
template<typename Func, Quantity Q1, Quantity Q2>
requires detail::InvocableQuantities<Func, Q1, Q2>
using common_quantity_for = quantity<common_reference(Q1::reference, Q2::reference),
std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
} // namespace detail } // namespace detail
/** /**
@ -341,24 +327,54 @@ explicit quantity(Q) -> quantity<quantity_like_traits<Q>::reference, typename qu
// binary operators on quantities // binary operators on quantities
template<auto R1, typename Rep1, auto R2, typename Rep2> template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvocableQuantities<std::plus<>, quantity<R1, Rep1>, quantity<R2, Rep2>> requires requires(Rep1 a, Rep2 b) {
common_reference(R1, R2);
{
a + b
} -> detail::IsOfCharacter<get_quantity_spec(R1).character>
}
[[nodiscard]] constexpr Quantity auto operator+(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs) [[nodiscard]] constexpr Quantity auto operator+(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{ {
using ret = detail::common_quantity_for<std::plus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>; using ret = quantity<common_reference(R1, R2), decltype(lhs.number() + rhs.number())>;
return make_quantity<ret::reference>(ret(lhs).number() + ret(rhs).number()); return make_quantity<ret::reference>(ret(lhs).number() + ret(rhs).number());
} }
template<auto R1, typename Rep1, auto R2, typename Rep2> template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvocableQuantities<std::minus<>, quantity<R1, Rep1>, quantity<R2, Rep2>> requires requires(Rep1 a, Rep2 b) {
common_reference(R1, R2);
{
a - b
} -> detail::IsOfCharacter<get_quantity_spec(R1).character>
}
[[nodiscard]] constexpr Quantity auto operator-(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs) [[nodiscard]] constexpr Quantity auto operator-(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{ {
using ret = detail::common_quantity_for<std::minus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>; using ret = quantity<common_reference(R1, R2), decltype(lhs.number() - rhs.number())>;
return make_quantity<ret::reference>(ret(lhs).number() - ret(rhs).number()); return make_quantity<ret::reference>(ret(lhs).number() - ret(rhs).number());
} }
template<ReferenceOf<quantity_character::scalar> auto R1, typename Rep1,
ReferenceOf<quantity_character::scalar> auto R2, typename Rep2>
requires(!treat_as_floating_point<Rep1>) && (!treat_as_floating_point<Rep2>) && requires(Rep1 a, Rep2 b) {
common_reference(R1, R2);
{
a % b
} -> detail::IsOfCharacter<quantity_character::scalar>
}
[[nodiscard]] constexpr Quantity auto operator%(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<Rep1>::zero());
using ret = quantity<common_reference(R1, R2), decltype(lhs.number() % rhs.number())>;
return make_quantity<ret::reference>(ret(lhs).number() % ret(rhs).number());
}
template<auto R1, typename Rep1, auto R2, typename Rep2> template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvokeResultOf<(get_quantity_spec(R1) * get_quantity_spec(R2)).character, std::multiplies<>, Rep1, requires(ReferenceOf<std::remove_const_t<decltype(R1)>, quantity_character::scalar> ||
Rep2> ReferenceOf<std::remove_const_t<decltype(R2)>, quantity_character::scalar>) &&
requires(Rep1 a, Rep2 b) {
{
a* b
} -> detail::IsOfCharacter<(get_quantity_spec(R1) * get_quantity_spec(R2)).character>
}
[[nodiscard]] constexpr Quantity auto operator*(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs) [[nodiscard]] constexpr Quantity auto operator*(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{ {
return make_quantity<R1 * R2>(lhs.number() * rhs.number()); return make_quantity<R1 * R2>(lhs.number() * rhs.number());
@ -366,21 +382,30 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
template<auto R, typename Rep, typename Value> template<auto R, typename Rep, typename Value>
requires(!Quantity<Value>) && requires(!Quantity<Value>) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::multiplies<>, Rep, const Value&> (ReferenceOf<std::remove_const_t<decltype(R)>, quantity_character::scalar> || detail::Scalar<Value>) &&
[[nodiscard]] constexpr Quantity auto operator*(const quantity<R, Rep>& q, const Value& v) requires(Rep a, Value b) {
{
a* b
} -> detail::IsOfCharacter<(get_quantity_spec(R) * detail::get_character(b)).character>
}
detail::InvokeResultOf<get_quantity_spec(R).character, std::multiplies<>, Rep,
const Value&> [[nodiscard]] constexpr Quantity auto
operator*(const quantity<R, Rep>& q, const Value& v)
{ {
return make_quantity<R>(q.number() * v); return make_quantity<R>(q.number() * v);
} }
template<typename Value, auto R, typename Rep> template<typename Value, auto R, typename Rep>
requires(!Quantity<Value>) && requires(!Quantity<Value>) &&
(ReferenceOf<std::remove_const_t<decltype(R)>, quantity_character::scalar> || detail::Scalar<Value>) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::multiplies<>, const Value&, Rep> detail::InvokeResultOf<get_quantity_spec(R).character, std::multiplies<>, const Value&, Rep>
[[nodiscard]] constexpr Quantity auto operator*(const Value& v, const quantity<R, Rep>& q) [[nodiscard]] constexpr Quantity auto operator*(const Value& v, const quantity<R, Rep>& q)
{ {
return make_quantity<R>(v * q.number()); return make_quantity<R>(v * q.number());
} }
template<auto R1, typename Rep1, auto R2, typename Rep2> template<auto R1, typename Rep1, ReferenceOf<quantity_character::scalar> auto R2, typename Rep2>
requires detail::InvokeResultOf<(get_quantity_spec(R1) / get_quantity_spec(R2)).character, std::divides<>, Rep1, Rep2> requires detail::InvokeResultOf<(get_quantity_spec(R1) / get_quantity_spec(R2)).character, std::divides<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator/(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs) [[nodiscard]] constexpr Quantity auto operator/(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{ {
@ -405,14 +430,41 @@ template<typename Value, auto R, typename Rep>
return make_quantity<::mp_units::one / R>(v / q.number()); return make_quantity<::mp_units::one / R>(v / q.number());
} }
template<auto R1, typename Rep1, auto R2, typename Rep2> template<ReferenceOf<quantity_character::vector> auto R1, typename Rep1,
requires(!treat_as_floating_point<Rep1>) && (!treat_as_floating_point<Rep2>) && ReferenceOf<quantity_character::vector> auto R2, typename Rep2>
detail::InvocableQuantities<std::modulus<>, quantity<R1, Rep1>, quantity<R2, Rep2>> requires requires(Rep1 v1, Rep2 v2) {
[[nodiscard]] constexpr Quantity auto operator%(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs) {
dot_product(v1, v2)
} -> detail::Vector;
}
[[nodiscard]] QuantityOf<dot_product(get_quantity_spec(R1), get_quantity_spec(R2))> auto dot_product(
const quantity<R1, Rep1>& q1, const quantity<R2, Rep2>& q2)
{ {
gsl_ExpectsAudit(rhs.number() != quantity_values<Rep1>::zero()); return make_quantity<dot_product(R1, R2)>(dot_product(q1.number(), q2.number()));
using ret = detail::common_quantity_for<std::modulus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>; }
return make_quantity<ret::reference>(ret(lhs).number() % ret(rhs).number());
template<ReferenceOf<quantity_character::vector> auto R1, typename Rep1,
ReferenceOf<quantity_character::vector> auto R2, typename Rep2>
requires requires(Rep1 v1, Rep2 v2) {
{
cross_product(v1, v2)
} -> detail::Scalar;
}
[[nodiscard]] QuantityOf<cross_product(get_quantity_spec(R1), get_quantity_spec(R2))> auto cross_product(
const quantity<R1, Rep1>& q1, const quantity<R2, Rep2>& q2)
{
return make_quantity<cross_product(R1, R2)>(cross_product(q1.number(), q2.number()));
}
template<ReferenceOf<quantity_character::vector> auto R, typename Rep>
requires requires(Rep v) {
{
norm(v)
} -> detail::Scalar;
}
[[nodiscard]] QuantityOf<norm(get_quantity_spec(R))> auto norm(const quantity<R, Rep>& q)
{
return make_quantity<norm(R)>(norm(q.number()));
} }
template<auto R1, typename Rep1, auto R2, typename Rep2> template<auto R1, typename Rep1, auto R2, typename Rep2>
@ -492,6 +544,7 @@ template<auto R, typename Rep, typename Value>
return q <=> make_quantity<::mp_units::one>(v); return q <=> make_quantity<::mp_units::one>(v);
} }
// make_quantity // make_quantity
template<Reference auto R, typename Rep> template<Reference auto R, typename Rep>
requires quantity<R, std::remove_cvref_t<Rep>>::_rep_safe_constructible_ requires quantity<R, std::remove_cvref_t<Rep>>::_rep_safe_constructible_

View File

@ -275,19 +275,20 @@ struct quantity_spec<Self, Eq, Args...> : detail::quantity_spec_interface<Self>
* errors. Having them of the same names improves user experience and somehow blurs those separate domains. * errors. Having them of the same names improves user experience and somehow blurs those separate domains.
* *
* @tparam Q quantity specification of a parent quantity * @tparam Q quantity specification of a parent quantity
* @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar * @tparam Args optionally `is_kind` in case the quantity starts a new hierarchy tree of a kind
* or `is_kind` in case the quantity starts a new hierarchy tree of a kind
*/ */
#ifdef __cpp_explicit_this_parameter #ifdef __cpp_explicit_this_parameter
template<detail::NamedQuantitySpec auto QS, one_of<quantity_character, struct is_kind> auto... Args> template<detail::NamedQuantitySpec auto QS, one_of<struct is_kind> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>) requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
struct quantity_spec<QS, Args...> : std::remove_const_t<decltype(QS)> { struct quantity_spec<QS, Args...> : std::remove_const_t<decltype(QS)> {
#else #else
// template<typename Self, detail::NamedQuantitySpec auto QS, one_of<struct is_kind> auto... Args>
template<typename Self, detail::NamedQuantitySpec auto QS, one_of<quantity_character, struct is_kind> auto... Args> template<typename Self, detail::NamedQuantitySpec auto QS, one_of<quantity_character, struct is_kind> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>) requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
struct quantity_spec<Self, QS, Args...> : std::remove_const_t<decltype(QS)> { struct quantity_spec<Self, QS, Args...> : std::remove_const_t<decltype(QS)> {
#endif #endif
static constexpr auto _parent_ = QS; static constexpr auto _parent_ = QS;
// static constexpr quantity_character character = QS.character;
static constexpr quantity_character character = detail::quantity_character_init<Args...>(QS.character); static constexpr quantity_character character = detail::quantity_character_init<Args...>(QS.character);
#ifndef __cpp_explicit_this_parameter #ifndef __cpp_explicit_this_parameter
@ -488,6 +489,7 @@ template<QuantitySpec auto... From, QuantitySpec Q>
// Operators // Operators
[[nodiscard]] consteval QuantitySpec auto operator*(QuantitySpec auto lhs, QuantitySpec auto rhs) [[nodiscard]] consteval QuantitySpec auto operator*(QuantitySpec auto lhs, QuantitySpec auto rhs)
requires(lhs.character == quantity_character::scalar || rhs.character == quantity_character::scalar)
{ {
return clone_kind_of<lhs, rhs>( return clone_kind_of<lhs, rhs>(
detail::expr_multiply<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>( detail::expr_multiply<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
@ -496,6 +498,7 @@ template<QuantitySpec auto... From, QuantitySpec Q>
template<QuantitySpec Lhs, QuantitySpec Rhs> template<QuantitySpec Lhs, QuantitySpec Rhs>
[[nodiscard]] consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs) [[nodiscard]] consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs)
requires(rhs.character == quantity_character::scalar)
{ {
return clone_kind_of<lhs, rhs>( return clone_kind_of<lhs, rhs>(
detail::expr_divide<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>( detail::expr_divide<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
@ -503,6 +506,7 @@ template<QuantitySpec Lhs, QuantitySpec Rhs>
} }
[[nodiscard]] consteval QuantitySpec auto operator/(int value, QuantitySpec auto q) [[nodiscard]] consteval QuantitySpec auto operator/(int value, QuantitySpec auto q)
requires(q.character == quantity_character::scalar)
{ {
gsl_Expects(value == 1); gsl_Expects(value == 1);
return clone_kind_of<q>(detail::expr_invert<derived_quantity_spec, struct dimensionless>(q)); return clone_kind_of<q>(detail::expr_invert<derived_quantity_spec, struct dimensionless>(q));
@ -516,6 +520,28 @@ template<QuantitySpec Lhs, QuantitySpec Rhs>
return is_same_v<Lhs, Rhs>; return is_same_v<Lhs, Rhs>;
} }
[[nodiscard]] consteval QuantitySpec auto dot_product(QuantitySpec auto lhs, QuantitySpec auto rhs)
requires(lhs.character == quantity_character::vector && rhs.character == quantity_character::vector)
{
return clone_kind_of<lhs, rhs>(
detail::expr_multiply<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
remove_kind(lhs), remove_kind(rhs)));
}
[[nodiscard]] consteval QuantitySpec auto cross_product(QuantitySpec auto lhs, QuantitySpec auto rhs)
requires(lhs.character == quantity_character::vector && rhs.character == quantity_character::vector)
{
return clone_kind_of<lhs, rhs>(
detail::expr_multiply<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
remove_kind(lhs), remove_kind(rhs)));
}
[[nodiscard]] consteval QuantitySpec auto norm(QuantitySpec auto q)
requires(q.character == quantity_character::vector)
{
return q;
}
template<detail::QuantityKindSpec Lhs, detail::QuantityKindSpec Rhs> template<detail::QuantityKindSpec Lhs, detail::QuantityKindSpec Rhs>
[[nodiscard]] consteval bool operator==(Lhs, Rhs) [[nodiscard]] consteval bool operator==(Lhs, Rhs)
{ {
@ -1475,7 +1501,8 @@ template<QuantitySpec Q>
template<QuantitySpec Q1, QuantitySpec Q2> template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2) [[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2)
requires(implicitly_convertible(get_kind(q1), get_kind(q2)) || implicitly_convertible(get_kind(q2), get_kind(q1))) requires(Q1::character == Q2::character) &&
(implicitly_convertible(get_kind(q1), get_kind(q2)) || implicitly_convertible(get_kind(q2), get_kind(q1)))
{ {
using QQ1 = std::remove_const_t<decltype(remove_kind(q1))>; using QQ1 = std::remove_const_t<decltype(remove_kind(q1))>;
using QQ2 = std::remove_const_t<decltype(remove_kind(q2))>; using QQ2 = std::remove_const_t<decltype(remove_kind(q2))>;

View File

@ -52,10 +52,10 @@ QUANTITY_SPEC(electric_flux_density, electric_polarization); // vector
inline constexpr auto electric_displacement = electric_flux_density; inline constexpr auto electric_displacement = electric_flux_density;
QUANTITY_SPEC(capacitance, electric_charge / voltage); QUANTITY_SPEC(capacitance, electric_charge / voltage);
// TODO how to calculate an argument of a vector product? // TODO how to calculate an argument of a vector product?
QUANTITY_SPEC(magnetic_flux_density, force / (electric_charge * velocity), quantity_character::vector); // QUANTITY_SPEC(magnetic_flux_density, force / (electric_charge * velocity), quantity_character::vector);
QUANTITY_SPEC(magnetic_vector_potential, // QUANTITY_SPEC(magnetic_vector_potential,
magnetic_flux_density* length); // vector // TODO what is a correct equation here? // magnetic_flux_density* length); // vector // TODO what is a correct equation here?
QUANTITY_SPEC(linked_flux, magnetic_vector_potential* displacement, quantity_character::scalar); // QUANTITY_SPEC(linked_flux, magnetic_vector_potential* displacement, quantity_character::scalar);
QUANTITY_SPEC(magnetic_constant, QUANTITY_SPEC(magnetic_constant,
electric_potential* time / (electric_current * length)); // TODO what is a correct equation here? electric_potential* time / (electric_current * length)); // TODO what is a correct equation here?
inline constexpr auto permeability_of_vacuum = magnetic_constant; inline constexpr auto permeability_of_vacuum = magnetic_constant;
@ -64,7 +64,7 @@ QUANTITY_SPEC(speed_of_light, speed);
inline constexpr auto light_speed = speed_of_light; inline constexpr auto light_speed = speed_of_light;
QUANTITY_SPEC(electric_constant, 1 / (magnetic_constant * pow<2>(speed_of_light))); QUANTITY_SPEC(electric_constant, 1 / (magnetic_constant * pow<2>(speed_of_light)));
inline constexpr auto permittivity_of_vacuum = electric_constant; inline constexpr auto permittivity_of_vacuum = electric_constant;
QUANTITY_SPEC(permittivity, electric_flux_density / electric_field_strength, quantity_character::scalar); QUANTITY_SPEC(permittivity, norm(electric_flux_density) / norm(electric_field_strength), quantity_character::scalar);
QUANTITY_SPEC(relative_permittivity, dimensionless, permittivity / electric_constant); QUANTITY_SPEC(relative_permittivity, dimensionless, permittivity / electric_constant);
QUANTITY_SPEC(electric_susceptibility, dimensionless, QUANTITY_SPEC(electric_susceptibility, dimensionless,
electric_polarization / electric_constant / electric_field_strength, quantity_character::scalar); electric_polarization / electric_constant / electric_field_strength, quantity_character::scalar);
@ -73,13 +73,13 @@ QUANTITY_SPEC(displacement_current_density, electric_flux_density / time); // v
QUANTITY_SPEC(displacement_current, electric_current, displacement_current_density* area, quantity_character::scalar); QUANTITY_SPEC(displacement_current, electric_current, displacement_current_density* area, quantity_character::scalar);
QUANTITY_SPEC(total_current, electric_current); QUANTITY_SPEC(total_current, electric_current);
QUANTITY_SPEC(total_current_density, electric_current_density); // vector QUANTITY_SPEC(total_current_density, electric_current_density); // vector
QUANTITY_SPEC(magnetic_flux, magnetic_flux_density* area, quantity_character::scalar); // QUANTITY_SPEC(magnetic_flux, magnetic_flux_density* area, quantity_character::scalar);
QUANTITY_SPEC(magnetic_moment, electric_current* area, quantity_character::vector); QUANTITY_SPEC(magnetic_moment, electric_current* area, quantity_character::vector);
inline constexpr auto magnetic_area_moment = magnetic_moment; inline constexpr auto magnetic_area_moment = magnetic_moment;
QUANTITY_SPEC(magnetization, magnetic_moment / volume); // vector QUANTITY_SPEC(magnetization, magnetic_moment / volume); // vector
QUANTITY_SPEC(magnetic_field_strength, magnetization); // vector QUANTITY_SPEC(magnetic_field_strength, magnetization); // vector
inline constexpr auto magnetizing_field = magnetic_field_strength; inline constexpr auto magnetizing_field = magnetic_field_strength;
QUANTITY_SPEC(permeability, magnetic_flux_density / magnetic_field_strength, quantity_character::scalar); // QUANTITY_SPEC(permeability, magnetic_flux_density / magnetic_field_strength, quantity_character::scalar);
QUANTITY_SPEC(relative_permeability, dimensionless, permeability / magnetic_constant); QUANTITY_SPEC(relative_permeability, dimensionless, permeability / magnetic_constant);
QUANTITY_SPEC(magnetic_susceptibility, dimensionless, magnetization / magnetic_field_strength, QUANTITY_SPEC(magnetic_susceptibility, dimensionless, magnetization / magnetic_field_strength,
quantity_character::scalar); quantity_character::scalar);
@ -101,15 +101,15 @@ QUANTITY_SPEC(current_linkage, electric_current);
QUANTITY_SPEC(number_of_turns_in_a_winding, dimensionless); QUANTITY_SPEC(number_of_turns_in_a_winding, dimensionless);
QUANTITY_SPEC(reluctance, magnetic_tension / magnetic_flux); QUANTITY_SPEC(reluctance, magnetic_tension / magnetic_flux);
QUANTITY_SPEC(permeance, 1 / reluctance); QUANTITY_SPEC(permeance, 1 / reluctance);
QUANTITY_SPEC(inductance, linked_flux / electric_current); // QUANTITY_SPEC(inductance, linked_flux / electric_current);
inline constexpr auto self_inductance = inductance; // inline constexpr auto self_inductance = inductance;
QUANTITY_SPEC(mutual_inductance, linked_flux / electric_current); // QUANTITY_SPEC(mutual_inductance, linked_flux / electric_current);
QUANTITY_SPEC(coupling_factor, dimensionless, mutual_inductance / pow<1, 2>(pow<2>(self_inductance))); QUANTITY_SPEC(coupling_factor, dimensionless, mutual_inductance / pow<1, 2>(pow<2>(self_inductance)));
QUANTITY_SPEC(leakage_factor, dimensionless, pow<2>(coupling_factor)); QUANTITY_SPEC(leakage_factor, dimensionless, pow<2>(coupling_factor));
QUANTITY_SPEC(conductivity, electric_current_density / electric_field_strength, quantity_character::scalar); QUANTITY_SPEC(conductivity, electric_current_density / electric_field_strength, quantity_character::scalar);
QUANTITY_SPEC(resistivity, 1 / conductivity); QUANTITY_SPEC(resistivity, 1 / conductivity);
// QUANTITY_SPEC(power, voltage* electric_current); // TODO conflicts with mechanical power QUANTITY_SPEC(electric_power, power, voltage* electric_current);
// inline constexpr auto instantaneous_power = power; inline constexpr auto instantaneous_power = electric_power;
QUANTITY_SPEC(instantaneous_power, voltage* electric_current); QUANTITY_SPEC(instantaneous_power, voltage* electric_current);
QUANTITY_SPEC(resistance, voltage / electric_current); QUANTITY_SPEC(resistance, voltage / electric_current);
QUANTITY_SPEC(conductance, 1 / resistance); QUANTITY_SPEC(conductance, 1 / resistance);

View File

@ -49,49 +49,53 @@ inline constexpr auto rolling_drag = rolling_resistance;
inline constexpr auto rolling_friction_force = rolling_resistance; inline constexpr auto rolling_friction_force = rolling_resistance;
QUANTITY_SPEC(drag_force, force); // vector QUANTITY_SPEC(drag_force, force); // vector
QUANTITY_SPEC(impulse, force* time); // vector QUANTITY_SPEC(impulse, force* time); // vector
QUANTITY_SPEC(angular_momentum, position_vector* momentum); // vector QUANTITY_SPEC(angular_momentum, cross_product(position_vector, momentum)); // vector
QUANTITY_SPEC(moment_of_inertia, angular_momentum / angular_velocity, quantity_character::tensor); // QUANTITY_SPEC(moment_of_inertia, tensor_product(angular_momentum, angular_velocity), quantity_character::tensor);
QUANTITY_SPEC(moment_of_force, position_vector* force); // vector QUANTITY_SPEC(moment_of_force, cross_product(position_vector, force)); // vector
QUANTITY_SPEC(torque, moment_of_force, quantity_character::scalar); QUANTITY_SPEC(torque, moment_of_force, quantity_character::scalar);
QUANTITY_SPEC(angular_impulse, moment_of_force* time); // vector QUANTITY_SPEC(angular_impulse, moment_of_force* time); // vector
QUANTITY_SPEC(pressure, force / area, quantity_character::scalar); QUANTITY_SPEC(pressure, force / area, quantity_character::scalar);
QUANTITY_SPEC(gauge_pressure, pressure); QUANTITY_SPEC(gauge_pressure, pressure);
QUANTITY_SPEC(stress, pressure, quantity_character::tensor); QUANTITY_SPEC(stress, pressure, quantity_character::tensor); // TODO what is a correct equation here?
QUANTITY_SPEC(normal_stress, pressure, quantity_character::scalar); // QUANTITY_SPEC(normal_stress, pressure, quantity_character::scalar);
QUANTITY_SPEC(shear_stress, pressure, quantity_character::scalar); // QUANTITY_SPEC(shear_stress, pressure, quantity_character::scalar);
QUANTITY_SPEC(strain, dimensionless, quantity_character::tensor); // QUANTITY_SPEC(strain, dimensionless, quantity_character::tensor);
QUANTITY_SPEC(relative_linear_strain, length / length); QUANTITY_SPEC(relative_linear_strain, length / length);
QUANTITY_SPEC(shear_strain, dimensionless, displacement / thickness, quantity_character::scalar); // QUANTITY_SPEC(shear_strain, dimensionless, displacement / thickness, quantity_character::scalar);
QUANTITY_SPEC(relative_volume_strain, volume / volume); QUANTITY_SPEC(relative_volume_strain, volume / volume);
QUANTITY_SPEC(Poisson_number, dimensionless, width / length); QUANTITY_SPEC(Poisson_number, dimensionless, width / length);
QUANTITY_SPEC(modulus_of_elasticity, normal_stress / relative_linear_strain); // QUANTITY_SPEC(modulus_of_elasticity, normal_stress / relative_linear_strain);
inline constexpr auto Young_modulus = modulus_of_elasticity; // inline constexpr auto Young_modulus = modulus_of_elasticity;
QUANTITY_SPEC(modulus_of_rigidity, shear_stress / shear_strain); // QUANTITY_SPEC(modulus_of_rigidity, shear_stress / shear_strain);
inline constexpr auto shear_modulus = modulus_of_rigidity; // inline constexpr auto shear_modulus = modulus_of_rigidity;
QUANTITY_SPEC(modulus_of_compression, pressure / relative_volume_strain); QUANTITY_SPEC(modulus_of_compression, pressure / relative_volume_strain);
inline constexpr auto bulk_modulus = modulus_of_compression; inline constexpr auto bulk_modulus = modulus_of_compression;
QUANTITY_SPEC(compressibility, 1 / volume * (volume / pressure)); QUANTITY_SPEC(compressibility, 1 / volume * (volume / pressure));
QUANTITY_SPEC(second_axial_moment_of_area, pow<2>(radial_distance) * area); QUANTITY_SPEC(second_axial_moment_of_area, pow<2>(radial_distance) * area);
QUANTITY_SPEC(second_polar_moment_of_area, pow<2>(radial_distance) * area); QUANTITY_SPEC(second_polar_moment_of_area, pow<2>(radial_distance) * area);
QUANTITY_SPEC(section_modulus, second_axial_moment_of_area / radial_distance); QUANTITY_SPEC(section_modulus, second_axial_moment_of_area / radial_distance);
QUANTITY_SPEC(static_friction_coefficient, dimensionless, static_friction_force / force, quantity_character::scalar); QUANTITY_SPEC(static_friction_coefficient, dimensionless, norm(static_friction_force) / norm(force),
quantity_character::scalar);
inline constexpr auto static_friction_factor = static_friction_coefficient; inline constexpr auto static_friction_factor = static_friction_coefficient;
inline constexpr auto coefficient_of_static_friction = static_friction_coefficient; inline constexpr auto coefficient_of_static_friction = static_friction_coefficient;
QUANTITY_SPEC(kinetic_friction_factor, dimensionless, kinetic_friction_force / force, quantity_character::scalar); QUANTITY_SPEC(kinetic_friction_factor, dimensionless, norm(kinetic_friction_force) / norm(force),
quantity_character::scalar);
inline constexpr auto dynamic_friction_factor = kinetic_friction_factor; inline constexpr auto dynamic_friction_factor = kinetic_friction_factor;
QUANTITY_SPEC(rolling_resistance_factor, force / force, quantity_character::scalar); QUANTITY_SPEC(rolling_resistance_factor, norm(force) / norm(force), quantity_character::scalar);
QUANTITY_SPEC(drag_coefficient, dimensionless, drag_force / (mass_density * pow<2>(speed) * area), QUANTITY_SPEC(drag_coefficient, dimensionless, norm(drag_force) / (mass_density * pow<2>(speed) * area),
quantity_character::scalar); quantity_character::scalar);
inline constexpr auto drag_factor = drag_coefficient; inline constexpr auto drag_factor = drag_coefficient;
QUANTITY_SPEC(dynamic_viscosity, shear_stress* length / velocity, quantity_character::scalar); // QUANTITY_SPEC(dynamic_viscosity, shear_stress* length / velocity, quantity_character::scalar);
QUANTITY_SPEC(kinematic_viscosity, dynamic_viscosity / mass_density); // QUANTITY_SPEC(kinematic_viscosity, dynamic_viscosity / mass_density);
QUANTITY_SPEC(surface_tension, force / length, quantity_character::scalar); // TODO what is a correct equation here? QUANTITY_SPEC(surface_tension, nrom(force) / length, quantity_character::scalar);
QUANTITY_SPEC(power, force* velocity, quantity_character::scalar); QUANTITY_SPEC(power, mass* pow<2>(length) / pow<3>(time)); // differs from ISO 80000
QUANTITY_SPEC(mechanical_power, power, dot_product(force, velocity),
quantity_character::scalar); // differs from ISO 80000
QUANTITY_SPEC(energy, mass* pow<2>(length) / pow<2>(time)); // ISO 80000 defines this in thermodynamics QUANTITY_SPEC(energy, mass* pow<2>(length) / pow<2>(time)); // ISO 80000 defines this in thermodynamics
QUANTITY_SPEC(mechanical_energy, energy); // differs from ISO 80000 QUANTITY_SPEC(mechanical_energy, energy); // differs from ISO 80000
QUANTITY_SPEC(potential_energy, mechanical_energy); // differs from ISO 80000 QUANTITY_SPEC(potential_energy, mechanical_energy); // differs from ISO 80000
QUANTITY_SPEC(kinetic_energy, mechanical_energy, mass* pow<2>(speed)); // differs from ISO 80000 QUANTITY_SPEC(kinetic_energy, mechanical_energy, mass* pow<2>(speed)); // differs from ISO 80000
QUANTITY_SPEC(mechanical_work, force* displacement, quantity_character::scalar); QUANTITY_SPEC(mechanical_work, dot_product(force, displacement), quantity_character::scalar);
inline constexpr auto work = mechanical_work; inline constexpr auto work = mechanical_work;
QUANTITY_SPEC(efficiency_mechanics, power / power); QUANTITY_SPEC(efficiency_mechanics, power / power);
QUANTITY_SPEC(mass_flow, mass_density* velocity); // vector QUANTITY_SPEC(mass_flow, mass_density* velocity); // vector

View File

@ -35,9 +35,6 @@
template<typename Rep = double> template<typename Rep = double>
using vector = STD_LA::fixed_size_column_vector<Rep, 3>; using vector = STD_LA::fixed_size_column_vector<Rep, 3>;
template<typename Rep>
inline constexpr bool mp_units::is_vector<vector<Rep>> = true;
namespace STD_LA { namespace STD_LA {
template<typename Rep> template<typename Rep>
@ -51,35 +48,58 @@ std::ostream& operator<<(std::ostream& os, const ::vector<Rep>& v)
return os; return os;
} }
} // namespace STD_LA template<typename Rep1, typename Rep2, auto N>
[[nodiscard]] decltype(Rep1{} * Rep2{}) dot(const fixed_size_column_vector<Rep1, N>& a,
namespace { const fixed_size_column_vector<Rep2, N>& b)
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
template<typename T>
[[nodiscard]] auto get_magnitude(const vector<T>& v)
{ {
using namespace std; return 42;
return hypot(v(0), v(1), v(2));
} }
template<typename T, typename U> template<typename Rep1, typename Rep2, auto N>
[[nodiscard]] vector<decltype(T{} * U{})> cross_product(const vector<T>& a, const vector<U>& b) [[nodiscard]] fixed_size_column_vector<decltype(Rep1{} * Rep2{}), N> cross(const fixed_size_column_vector<Rep1, N>& a,
const fixed_size_column_vector<Rep2, N>& b)
{ {
return {a(1) * b(2) - a(2) * b(1), a(2) * b(0) - a(0) * b(2), a(0) * b(1) - a(1) * b(0)}; return {a(1) * b(2) - a(2) * b(1), a(2) * b(0) - a(0) * b(2), a(0) * b(1) - a(1) * b(0)};
} }
template<Quantity Q1, Quantity Q2> namespace detail {
requires is_vector<typename Q1::rep> && is_vector<typename Q2::rep> &&
requires(typename Q1::rep v1, typename Q2::rep v2) { cross_product(v1, v2); } template<typename Rep, auto N, std::size_t... I>
[[nodiscard]] QuantityOf<Q1::quantity_spec * Q2::quantity_spec> auto cross_product(const Q1& q1, const Q2& q2) [[nodiscard]] auto norm_impl(const fixed_size_column_vector<Rep, N>& v, std::index_sequence<I...>)
{ {
return cross_product(q1.number(), q2.number()) * (Q1::reference * Q2::reference); using namespace std;
return sqrt((... + (v(I) * v(I))));
} }
} // namespace } // namespace detail
template<typename Rep, auto N>
[[nodiscard]] auto norm(const fixed_size_column_vector<Rep, N>& v)
{
using namespace std;
if constexpr (N == 1)
return v(0);
else if constexpr (N == 2)
return hypot(v(0), v(1));
else if constexpr (N == 3)
return hypot(v(0), v(1), v(2));
else
return detail::norm_impl(v, std::make_index_sequence<N>{});
}
} // namespace STD_LA
template<typename Rep>
inline constexpr bool mp_units::is_vector<vector<Rep>> = true;
template<typename Rep, mp_units::Reference R>
[[nodiscard]] constexpr mp_units::quantity<R{}, vector<Rep>> operator*(const vector<Rep>& lhs, R)
{
return mp_units::make_quantity<R{}>(lhs);
}
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
TEST_CASE("vector quantity", "[la]") TEST_CASE("vector quantity", "[la]")
{ {
@ -101,7 +121,7 @@ TEST_CASE("vector quantity", "[la]")
SECTION("to scalar magnitude") SECTION("to scalar magnitude")
{ {
const auto v = vector<int>{2, 3, 6} * isq::velocity[km / h]; const auto v = vector<int>{2, 3, 6} * isq::velocity[km / h];
const auto speed = get_magnitude(v.number()) * isq::speed[v.unit]; // TODO can we do better here? const quantity<isq::speed[v.unit], int> speed = norm(v);
CHECK(speed.number() == 7); CHECK(speed.number() == 7);
} }
@ -122,12 +142,29 @@ TEST_CASE("vector quantity", "[la]")
} }
} }
SECTION("divide by scalar value") SECTION("multiply by dimensionless quantity")
{
const auto v = vector<int>{1, 2, 3} * isq::position_vector[m];
SECTION("integral")
{
SECTION("scalar on LHS") { CHECK(((2 * one) * v).number() == vector<int>{2, 4, 6}); }
SECTION("scalar on RHS") { CHECK((v * (2 * one)).number() == vector<int>{2, 4, 6}); }
}
SECTION("floating-point")
{
SECTION("scalar on LHS") { CHECK(((0.5 * one) * v).number() == vector<double>{0.5, 1., 1.5}); }
SECTION("scalar on RHS") { CHECK((v * (0.5 * one)).number() == vector<double>{0.5, 1., 1.5}); }
}
}
SECTION("divide by dimensionless quantity")
{ {
const auto v = vector<int>{2, 4, 6} * isq::position_vector[m]; const auto v = vector<int>{2, 4, 6} * isq::position_vector[m];
SECTION("integral") { CHECK((v / 2).number() == vector<int>{1, 2, 3}); } SECTION("integral") { CHECK((v / (2 * one)).number() == vector<int>{1, 2, 3}); }
SECTION("floating-point") { CHECK((v / 0.5).number() == vector<double>{4., 8., 12.}); } SECTION("floating-point") { CHECK((v / (0.5 * one)).number() == vector<double>{4., 8., 12.}); }
} }
SECTION("add") SECTION("add")
@ -273,7 +310,7 @@ TEST_CASE("vector quantity", "[la]")
const auto r = vector<int>{3, 0, 0} * isq::position_vector[m]; const auto r = vector<int>{3, 0, 0} * isq::position_vector[m];
const auto f = vector<int>{0, 10, 0} * isq::force[N]; const auto f = vector<int>{0, 10, 0} * isq::force[N];
CHECK(cross_product(r, f) == vector<int>{0, 0, 30} * isq::moment_of_force[N * m]); CHECK(cross(r, f) == vector<int>{0, 0, 30} * isq::moment_of_force[N * m]);
} }
} }
@ -299,7 +336,7 @@ TEST_CASE("vector of quantities", "[la]")
SECTION("to scalar magnitude") SECTION("to scalar magnitude")
{ {
const vector<quantity<isq::velocity[km / h], int>> v = {2 * (km / h), 3 * (km / h), 6 * (km / h)}; const vector<quantity<isq::velocity[km / h], int>> v = {2 * (km / h), 3 * (km / h), 6 * (km / h)};
const auto speed = get_magnitude(v).number() * isq::speed[v(0).unit]; // TODO can we do better here? const auto speed = norm(v).number() * isq::speed[v(0).unit]; // TODO can we do better here?
CHECK(speed.number() == 7); CHECK(speed.number() == 7);
} }
@ -335,6 +372,38 @@ TEST_CASE("vector of quantities", "[la]")
} }
} }
SECTION("multiply by dimensionless quantity")
{
const vector<quantity<isq::position_vector[m], int>> v = {1 * m, 2 * m, 3 * m};
SECTION("integral")
{
const vector<quantity<isq::position_vector[m], int>> result = {2 * m, 4 * m, 6 * m};
SECTION("scalar on LHS") { CHECK((2 * one) * v == result); }
SECTION("scalar on RHS") { CHECK(v * (2 * one) == result); }
}
SECTION("floating-point")
{
const vector<quantity<isq::position_vector[m], double>> result = {0.5 * m, 1. * m, 1.5 * m};
SECTION("scalar on LHS") { CHECK((0.5 * one) * v == result); }
SECTION("scalar on RHS") { CHECK(v * (0.5 * one) == result); }
}
}
SECTION("divide by dimensionless quantity")
{
const vector<quantity<isq::position_vector[m], int>> v = {2 * m, 4 * m, 6 * m};
SECTION("integral") { CHECK(v / (2 * one) == vector<quantity<isq::position_vector[m], int>>{1 * m, 2 * m, 3 * m}); }
SECTION("floating-point")
{
CHECK(v / (0.5 * one) == vector<quantity<isq::position_vector[m], double>>{4. * m, 8. * m, 12. * m});
}
}
SECTION("add") SECTION("add")
{ {
const vector<quantity<isq::position_vector[m], int>> v = {1 * m, 2 * m, 3 * m}; const vector<quantity<isq::position_vector[m], int>> v = {1 * m, 2 * m, 3 * m};

View File

@ -295,7 +295,7 @@ static_assert(!ReferenceOf<std::remove_const_t<decltype(dimensionless[one])>, is
// Representation // Representation
static_assert(Representation<int>); static_assert(Representation<int>);
static_assert(Representation<double>); static_assert(Representation<double>);
static_assert(Representation<std::complex<double>>); static_assert(!Representation<std::complex<double>>);
static_assert(!Representation<bool>); static_assert(!Representation<bool>);
static_assert(!Representation<std::optional<int>>); static_assert(!Representation<std::optional<int>>);
static_assert(!Representation<std::chrono::seconds>); static_assert(!Representation<std::chrono::seconds>);
@ -304,7 +304,7 @@ static_assert(!Representation<std::string>);
// RepresentationOf // RepresentationOf
static_assert(RepresentationOf<int, quantity_character::scalar>); static_assert(RepresentationOf<int, quantity_character::scalar>);
static_assert(RepresentationOf<double, quantity_character::scalar>); static_assert(RepresentationOf<double, quantity_character::scalar>);
static_assert(RepresentationOf<std::complex<double>, quantity_character::scalar>); static_assert(!RepresentationOf<std::complex<double>, quantity_character::scalar>);
static_assert(!RepresentationOf<bool, quantity_character::scalar>); static_assert(!RepresentationOf<bool, quantity_character::scalar>);
static_assert(!RepresentationOf<std::optional<int>, quantity_character::scalar>); static_assert(!RepresentationOf<std::optional<int>, quantity_character::scalar>);
static_assert(!RepresentationOf<std::chrono::seconds, quantity_character::scalar>); static_assert(!RepresentationOf<std::chrono::seconds, quantity_character::scalar>);