diff --git a/src/core/include/mp-units/bits/reference_concepts.h b/src/core/include/mp-units/bits/reference_concepts.h index c51d2d593..d3fdbaf35 100644 --- a/src/core/include/mp-units/bits/reference_concepts.h +++ b/src/core/include/mp-units/bits/reference_concepts.h @@ -77,6 +77,7 @@ concept ReferenceOf = (QuantitySpec> && implicitly_convertible(get_quantity_spec(T{}), V) && !detail::DerivedFromQuantityKindSpecOf && (detail::QuantityKindSpec> || - !detail::DerivedFromQuantityKindSpecOf))); + !detail::DerivedFromQuantityKindSpecOf)) || + (std::same_as, quantity_character> && (get_quantity_spec(T{}).character == V))); } // namespace mp_units diff --git a/src/core/include/mp-units/bits/representation_concepts.h b/src/core/include/mp-units/bits/representation_concepts.h index f25a0d9b6..36328a964 100644 --- a/src/core/include/mp-units/bits/representation_concepts.h +++ b/src/core/include/mp-units/bits/representation_concepts.h @@ -52,35 +52,88 @@ enum class quantity_character { scalar, vector, tensor }; namespace detail { -template -concept CommonTypeWith = - std::same_as, std::common_type_t> && - std::constructible_from, T> && std::constructible_from, U>; +template +concept Scalar = is_scalar; + +template +concept Vector = is_vector; + +template +concept Tensor = is_tensor; -template -concept ScalableNumber = - std::regular_invocable, T, U> && std::regular_invocable, T, U>; +template +concept IsOfCharacter = + (Ch == quantity_character::scalar && is_scalar) || (Ch == quantity_character::vector && is_vector) || + (Ch == quantity_character::tensor && is_tensor); + +template +[[nodiscard]] consteval quantity_character get_character(T) +{ + if constexpr (Tensor) + return quantity_character::tensor; + else if constexpr (Vector) + return quantity_character::vector; + else if constexpr (Scalar) + return quantity_character::scalar; +} + +// clang-format off +template +concept ScalarRepresentation = std::regular && Scalar && + 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 -concept CastableNumber = CommonTypeWith && ScalableNumber>; +concept VectorRepresentation = std::regular && Vector && + 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; + }; -// TODO Fix it according to sudo_cast implementation template -concept Scalable = - CastableNumber || - (requires { typename T::value_type; } && CastableNumber && - ScalableNumber>) || - (requires { typename T::element_type; } && CastableNumber> && - ScalableNumber, std::intmax_t>>); +concept TensorRepresentation = std::regular && Tensor && + 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 template -concept Representation = (is_scalar || is_vector || is_tensor)&&std::regular && detail::Scalable; +concept Representation = + detail::ScalarRepresentation || detail::VectorRepresentation || detail::TensorRepresentation; template -concept RepresentationOf = Representation && ((Ch == quantity_character::scalar && is_scalar) || - (Ch == quantity_character::vector && is_vector) || - (Ch == quantity_character::tensor && is_tensor)); +concept RepresentationOf = detail::IsOfCharacter && Representation; } // namespace mp_units diff --git a/src/core/include/mp-units/quantity.h b/src/core/include/mp-units/quantity.h index 850cf624e..083da70fe 100644 --- a/src/core/include/mp-units/quantity.h +++ b/src/core/include/mp-units/quantity.h @@ -65,20 +65,6 @@ concept QuantityConvertibleTo = // deduced thus the function is evaluated here and may emit truncating conversion or other warnings) requires(QFrom q) { detail::sudo_cast(q); }; -template -concept InvokeResultOf = std::regular_invocable && RepresentationOf, Ch>; - -template -concept InvocableQuantities = Quantity && Quantity && - InvokeResultOf && - requires { common_reference(Q1::reference, Q2::reference); }; - -template - requires detail::InvocableQuantities -using common_quantity_for = quantity>; - } // namespace detail /** @@ -341,24 +327,54 @@ explicit quantity(Q) -> quantity::reference, typename qu // binary operators on quantities template - requires detail::InvocableQuantities, quantity, quantity> + requires requires(Rep1 a, Rep2 b) { + common_reference(R1, R2); + { + a + b + } -> detail::IsOfCharacter + } [[nodiscard]] constexpr Quantity auto operator+(const quantity& lhs, const quantity& rhs) { - using ret = detail::common_quantity_for, quantity, quantity>; + using ret = quantity; return make_quantity(ret(lhs).number() + ret(rhs).number()); } template - requires detail::InvocableQuantities, quantity, quantity> + requires requires(Rep1 a, Rep2 b) { + common_reference(R1, R2); + { + a - b + } -> detail::IsOfCharacter + } [[nodiscard]] constexpr Quantity auto operator-(const quantity& lhs, const quantity& rhs) { - using ret = detail::common_quantity_for, quantity, quantity>; + using ret = quantity; return make_quantity(ret(lhs).number() - ret(rhs).number()); } +template auto R1, typename Rep1, + ReferenceOf auto R2, typename Rep2> + requires(!treat_as_floating_point) && (!treat_as_floating_point) && requires(Rep1 a, Rep2 b) { + common_reference(R1, R2); + { + a % b + } -> detail::IsOfCharacter + } +[[nodiscard]] constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs) +{ + gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); + using ret = quantity; + return make_quantity(ret(lhs).number() % ret(rhs).number()); +} + template - requires detail::InvokeResultOf<(get_quantity_spec(R1) * get_quantity_spec(R2)).character, std::multiplies<>, Rep1, - Rep2> + requires(ReferenceOf, quantity_character::scalar> || + ReferenceOf, 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& lhs, const quantity& rhs) { return make_quantity(lhs.number() * rhs.number()); @@ -366,21 +382,30 @@ template template requires(!Quantity) && - detail::InvokeResultOf, Rep, const Value&> -[[nodiscard]] constexpr Quantity auto operator*(const quantity& q, const Value& v) + (ReferenceOf, quantity_character::scalar> || detail::Scalar) && + requires(Rep a, Value b) { + { + a* b + } -> detail::IsOfCharacter<(get_quantity_spec(R) * detail::get_character(b)).character> + } + +detail::InvokeResultOf, Rep, + const Value&> [[nodiscard]] constexpr Quantity auto +operator*(const quantity& q, const Value& v) { return make_quantity(q.number() * v); } template requires(!Quantity) && + (ReferenceOf, quantity_character::scalar> || detail::Scalar) && detail::InvokeResultOf, const Value&, Rep> [[nodiscard]] constexpr Quantity auto operator*(const Value& v, const quantity& q) { return make_quantity(v * q.number()); } -template +template auto R2, typename Rep2> requires detail::InvokeResultOf<(get_quantity_spec(R1) / get_quantity_spec(R2)).character, std::divides<>, Rep1, Rep2> [[nodiscard]] constexpr Quantity auto operator/(const quantity& lhs, const quantity& rhs) { @@ -405,14 +430,41 @@ template return make_quantity<::mp_units::one / R>(v / q.number()); } -template - requires(!treat_as_floating_point) && (!treat_as_floating_point) && - detail::InvocableQuantities, quantity, quantity> -[[nodiscard]] constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs) +template auto R1, typename Rep1, + ReferenceOf auto R2, typename Rep2> + requires requires(Rep1 v1, Rep2 v2) { + { + dot_product(v1, v2) + } -> detail::Vector; + } +[[nodiscard]] QuantityOf auto dot_product( + const quantity& q1, const quantity& q2) { - gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); - using ret = detail::common_quantity_for, quantity, quantity>; - return make_quantity(ret(lhs).number() % ret(rhs).number()); + return make_quantity(dot_product(q1.number(), q2.number())); +} + +template auto R1, typename Rep1, + ReferenceOf auto R2, typename Rep2> + requires requires(Rep1 v1, Rep2 v2) { + { + cross_product(v1, v2) + } -> detail::Scalar; + } +[[nodiscard]] QuantityOf auto cross_product( + const quantity& q1, const quantity& q2) +{ + return make_quantity(cross_product(q1.number(), q2.number())); +} + +template auto R, typename Rep> + requires requires(Rep v) { + { + norm(v) + } -> detail::Scalar; + } +[[nodiscard]] QuantityOf auto norm(const quantity& q) +{ + return make_quantity(norm(q.number())); } template @@ -492,6 +544,7 @@ template return q <=> make_quantity<::mp_units::one>(v); } + // make_quantity template requires quantity>::_rep_safe_constructible_ diff --git a/src/core/include/mp-units/quantity_spec.h b/src/core/include/mp-units/quantity_spec.h index 0b3515081..ab6ce1325 100644 --- a/src/core/include/mp-units/quantity_spec.h +++ b/src/core/include/mp-units/quantity_spec.h @@ -275,19 +275,20 @@ struct quantity_spec : detail::quantity_spec_interface * 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 Args optionally a value of a `quantity_character` in case the base quantity should not be scalar - * or `is_kind` in case the quantity starts a new hierarchy tree of a kind + * @tparam Args optionally `is_kind` in case the quantity starts a new hierarchy tree of a kind */ #ifdef __cpp_explicit_this_parameter -template auto... Args> +template auto... Args> requires(... && !QuantitySpec>) struct quantity_spec : std::remove_const_t { #else +// template auto... Args> template auto... Args> requires(... && !QuantitySpec>) struct quantity_spec : std::remove_const_t { #endif static constexpr auto _parent_ = QS; + // static constexpr quantity_character character = QS.character; static constexpr quantity_character character = detail::quantity_character_init(QS.character); #ifndef __cpp_explicit_this_parameter @@ -488,6 +489,7 @@ template // Operators [[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( detail::expr_multiply( @@ -496,6 +498,7 @@ template template [[nodiscard]] consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs) + requires(rhs.character == quantity_character::scalar) { return clone_kind_of( detail::expr_divide( @@ -503,6 +506,7 @@ template } [[nodiscard]] consteval QuantitySpec auto operator/(int value, QuantitySpec auto q) + requires(q.character == quantity_character::scalar) { gsl_Expects(value == 1); return clone_kind_of(detail::expr_invert(q)); @@ -516,6 +520,28 @@ template return is_same_v; } +[[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( + detail::expr_multiply( + 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( + detail::expr_multiply( + remove_kind(lhs), remove_kind(rhs))); +} + +[[nodiscard]] consteval QuantitySpec auto norm(QuantitySpec auto q) + requires(q.character == quantity_character::vector) +{ + return q; +} + template [[nodiscard]] consteval bool operator==(Lhs, Rhs) { @@ -1475,7 +1501,8 @@ template template [[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; using QQ2 = std::remove_const_t; diff --git a/src/systems/isq/include/mp-units/systems/isq/electromagnetism.h b/src/systems/isq/include/mp-units/systems/isq/electromagnetism.h index 981b0b051..c383d14fd 100644 --- a/src/systems/isq/include/mp-units/systems/isq/electromagnetism.h +++ b/src/systems/isq/include/mp-units/systems/isq/electromagnetism.h @@ -52,10 +52,10 @@ QUANTITY_SPEC(electric_flux_density, electric_polarization); // vector inline constexpr auto electric_displacement = electric_flux_density; QUANTITY_SPEC(capacitance, electric_charge / voltage); // 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_vector_potential, - 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(magnetic_flux_density, force / (electric_charge * velocity), quantity_character::vector); +// QUANTITY_SPEC(magnetic_vector_potential, +// 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(magnetic_constant, electric_potential* time / (electric_current * length)); // TODO what is a correct equation here? 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; QUANTITY_SPEC(electric_constant, 1 / (magnetic_constant * pow<2>(speed_of_light))); 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(electric_susceptibility, dimensionless, 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(total_current, electric_current); 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); inline constexpr auto magnetic_area_moment = magnetic_moment; QUANTITY_SPEC(magnetization, magnetic_moment / volume); // vector QUANTITY_SPEC(magnetic_field_strength, magnetization); // vector 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(magnetic_susceptibility, dimensionless, magnetization / magnetic_field_strength, quantity_character::scalar); @@ -101,15 +101,15 @@ QUANTITY_SPEC(current_linkage, electric_current); QUANTITY_SPEC(number_of_turns_in_a_winding, dimensionless); QUANTITY_SPEC(reluctance, magnetic_tension / magnetic_flux); QUANTITY_SPEC(permeance, 1 / reluctance); -QUANTITY_SPEC(inductance, linked_flux / electric_current); -inline constexpr auto self_inductance = inductance; -QUANTITY_SPEC(mutual_inductance, linked_flux / electric_current); +// QUANTITY_SPEC(inductance, linked_flux / electric_current); +// inline constexpr auto self_inductance = inductance; +// QUANTITY_SPEC(mutual_inductance, linked_flux / electric_current); 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(conductivity, electric_current_density / electric_field_strength, quantity_character::scalar); QUANTITY_SPEC(resistivity, 1 / conductivity); -// QUANTITY_SPEC(power, voltage* electric_current); // TODO conflicts with mechanical power -// inline constexpr auto instantaneous_power = power; +QUANTITY_SPEC(electric_power, power, voltage* electric_current); +inline constexpr auto instantaneous_power = electric_power; QUANTITY_SPEC(instantaneous_power, voltage* electric_current); QUANTITY_SPEC(resistance, voltage / electric_current); QUANTITY_SPEC(conductance, 1 / resistance); diff --git a/src/systems/isq/include/mp-units/systems/isq/mechanics.h b/src/systems/isq/include/mp-units/systems/isq/mechanics.h index 2e9bda622..b68987484 100644 --- a/src/systems/isq/include/mp-units/systems/isq/mechanics.h +++ b/src/systems/isq/include/mp-units/systems/isq/mechanics.h @@ -47,51 +47,55 @@ inline constexpr auto dynamic_friction_force = kinetic_friction_force; QUANTITY_SPEC(rolling_resistance, force); // vector inline constexpr auto rolling_drag = rolling_resistance; inline constexpr auto rolling_friction_force = rolling_resistance; -QUANTITY_SPEC(drag_force, force); // vector -QUANTITY_SPEC(impulse, force* time); // vector -QUANTITY_SPEC(angular_momentum, position_vector* momentum); // vector -QUANTITY_SPEC(moment_of_inertia, angular_momentum / angular_velocity, quantity_character::tensor); -QUANTITY_SPEC(moment_of_force, position_vector* force); // vector +QUANTITY_SPEC(drag_force, force); // vector +QUANTITY_SPEC(impulse, force* time); // vector +QUANTITY_SPEC(angular_momentum, cross_product(position_vector, momentum)); // vector +// QUANTITY_SPEC(moment_of_inertia, tensor_product(angular_momentum, angular_velocity), quantity_character::tensor); +QUANTITY_SPEC(moment_of_force, cross_product(position_vector, force)); // vector QUANTITY_SPEC(torque, moment_of_force, quantity_character::scalar); QUANTITY_SPEC(angular_impulse, moment_of_force* time); // vector QUANTITY_SPEC(pressure, force / area, quantity_character::scalar); QUANTITY_SPEC(gauge_pressure, pressure); -QUANTITY_SPEC(stress, pressure, quantity_character::tensor); -QUANTITY_SPEC(normal_stress, pressure, quantity_character::scalar); -QUANTITY_SPEC(shear_stress, pressure, quantity_character::scalar); -QUANTITY_SPEC(strain, dimensionless, 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(shear_stress, pressure, quantity_character::scalar); +// QUANTITY_SPEC(strain, dimensionless, quantity_character::tensor); 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(Poisson_number, dimensionless, width / length); -QUANTITY_SPEC(modulus_of_elasticity, normal_stress / relative_linear_strain); -inline constexpr auto Young_modulus = modulus_of_elasticity; -QUANTITY_SPEC(modulus_of_rigidity, shear_stress / shear_strain); -inline constexpr auto shear_modulus = modulus_of_rigidity; +// QUANTITY_SPEC(modulus_of_elasticity, normal_stress / relative_linear_strain); +// inline constexpr auto Young_modulus = modulus_of_elasticity; +// QUANTITY_SPEC(modulus_of_rigidity, shear_stress / shear_strain); +// inline constexpr auto shear_modulus = modulus_of_rigidity; QUANTITY_SPEC(modulus_of_compression, pressure / relative_volume_strain); inline constexpr auto bulk_modulus = modulus_of_compression; QUANTITY_SPEC(compressibility, 1 / volume * (volume / pressure)); 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(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 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; -QUANTITY_SPEC(rolling_resistance_factor, force / force, quantity_character::scalar); -QUANTITY_SPEC(drag_coefficient, dimensionless, drag_force / (mass_density * pow<2>(speed) * area), +QUANTITY_SPEC(rolling_resistance_factor, norm(force) / norm(force), quantity_character::scalar); +QUANTITY_SPEC(drag_coefficient, dimensionless, norm(drag_force) / (mass_density * pow<2>(speed) * area), quantity_character::scalar); inline constexpr auto drag_factor = drag_coefficient; -QUANTITY_SPEC(dynamic_viscosity, shear_stress* length / velocity, quantity_character::scalar); -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(power, force* 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(surface_tension, nrom(force) / length, 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(mechanical_energy, 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(mechanical_work, force* displacement, quantity_character::scalar); +QUANTITY_SPEC(mechanical_work, dot_product(force, displacement), quantity_character::scalar); inline constexpr auto work = mechanical_work; QUANTITY_SPEC(efficiency_mechanics, power / power); QUANTITY_SPEC(mass_flow, mass_density* velocity); // vector diff --git a/test/unit_test/runtime/linear_algebra_test.cpp b/test/unit_test/runtime/linear_algebra_test.cpp index b0ee38f88..5b717e6bd 100644 --- a/test/unit_test/runtime/linear_algebra_test.cpp +++ b/test/unit_test/runtime/linear_algebra_test.cpp @@ -35,9 +35,6 @@ template using vector = STD_LA::fixed_size_column_vector; -template -inline constexpr bool mp_units::is_vector> = true; - namespace STD_LA { template @@ -51,35 +48,58 @@ std::ostream& operator<<(std::ostream& os, const ::vector& v) return os; } -} // namespace STD_LA +template +[[nodiscard]] decltype(Rep1{} * Rep2{}) dot(const fixed_size_column_vector& a, + const fixed_size_column_vector& b) +{ + return 42; +} -namespace { +template +[[nodiscard]] fixed_size_column_vector cross(const fixed_size_column_vector& a, + const fixed_size_column_vector& 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)}; +} -using namespace mp_units; -using namespace mp_units::si::unit_symbols; +namespace detail { -template -[[nodiscard]] auto get_magnitude(const vector& v) +template +[[nodiscard]] auto norm_impl(const fixed_size_column_vector& v, std::index_sequence) { using namespace std; - return hypot(v(0), v(1), v(2)); + return sqrt((... + (v(I) * v(I)))); } -template -[[nodiscard]] vector cross_product(const vector& a, const vector& b) +} // namespace detail + +template +[[nodiscard]] auto norm(const fixed_size_column_vector& v) { - 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)}; + 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{}); } -template - requires is_vector && is_vector && - requires(typename Q1::rep v1, typename Q2::rep v2) { cross_product(v1, v2); } -[[nodiscard]] QuantityOf auto cross_product(const Q1& q1, const Q2& q2) +} // namespace STD_LA + +template +inline constexpr bool mp_units::is_vector> = true; + +template +[[nodiscard]] constexpr mp_units::quantity> operator*(const vector& lhs, R) { - return cross_product(q1.number(), q2.number()) * (Q1::reference * Q2::reference); + return mp_units::make_quantity(lhs); } -} // namespace +using namespace mp_units; +using namespace mp_units::si::unit_symbols; TEST_CASE("vector quantity", "[la]") { @@ -101,7 +121,7 @@ TEST_CASE("vector quantity", "[la]") SECTION("to scalar magnitude") { const auto v = vector{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 speed = norm(v); 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{1, 2, 3} * isq::position_vector[m]; + + SECTION("integral") + { + SECTION("scalar on LHS") { CHECK(((2 * one) * v).number() == vector{2, 4, 6}); } + SECTION("scalar on RHS") { CHECK((v * (2 * one)).number() == vector{2, 4, 6}); } + } + + SECTION("floating-point") + { + SECTION("scalar on LHS") { CHECK(((0.5 * one) * v).number() == vector{0.5, 1., 1.5}); } + SECTION("scalar on RHS") { CHECK((v * (0.5 * one)).number() == vector{0.5, 1., 1.5}); } + } + } + + SECTION("divide by dimensionless quantity") { const auto v = vector{2, 4, 6} * isq::position_vector[m]; - SECTION("integral") { CHECK((v / 2).number() == vector{1, 2, 3}); } - SECTION("floating-point") { CHECK((v / 0.5).number() == vector{4., 8., 12.}); } + SECTION("integral") { CHECK((v / (2 * one)).number() == vector{1, 2, 3}); } + SECTION("floating-point") { CHECK((v / (0.5 * one)).number() == vector{4., 8., 12.}); } } SECTION("add") @@ -273,7 +310,7 @@ TEST_CASE("vector quantity", "[la]") const auto r = vector{3, 0, 0} * isq::position_vector[m]; const auto f = vector{0, 10, 0} * isq::force[N]; - CHECK(cross_product(r, f) == vector{0, 0, 30} * isq::moment_of_force[N * m]); + CHECK(cross(r, f) == vector{0, 0, 30} * isq::moment_of_force[N * m]); } } @@ -299,7 +336,7 @@ TEST_CASE("vector of quantities", "[la]") SECTION("to scalar magnitude") { const vector> 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); } @@ -335,6 +372,38 @@ TEST_CASE("vector of quantities", "[la]") } } + SECTION("multiply by dimensionless quantity") + { + const vector> v = {1 * m, 2 * m, 3 * m}; + + SECTION("integral") + { + const vector> 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> 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> v = {2 * m, 4 * m, 6 * m}; + + SECTION("integral") { CHECK(v / (2 * one) == vector>{1 * m, 2 * m, 3 * m}); } + SECTION("floating-point") + { + CHECK(v / (0.5 * one) == vector>{4. * m, 8. * m, 12. * m}); + } + } + SECTION("add") { const vector> v = {1 * m, 2 * m, 3 * m}; diff --git a/test/unit_test/static/concepts_test.cpp b/test/unit_test/static/concepts_test.cpp index a7b0a6da0..c57e266b3 100644 --- a/test/unit_test/static/concepts_test.cpp +++ b/test/unit_test/static/concepts_test.cpp @@ -295,7 +295,7 @@ static_assert(!ReferenceOf, is // Representation static_assert(Representation); static_assert(Representation); -static_assert(Representation>); +static_assert(!Representation>); static_assert(!Representation); static_assert(!Representation>); static_assert(!Representation); @@ -304,7 +304,7 @@ static_assert(!Representation); // RepresentationOf static_assert(RepresentationOf); static_assert(RepresentationOf); -static_assert(RepresentationOf, quantity_character::scalar>); +static_assert(!RepresentationOf, quantity_character::scalar>); static_assert(!RepresentationOf); static_assert(!RepresentationOf, quantity_character::scalar>); static_assert(!RepresentationOf);