Skip to content

Commit

Permalink
refactor: representation concepts refactored + some quantities switch…
Browse files Browse the repository at this point in the history
…ed to complex
  • Loading branch information
mpusz committed Nov 5, 2024
1 parent 1595fca commit c7303cc
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 30 deletions.
107 changes: 88 additions & 19 deletions src/core/include/mp-units/framework/representation_concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,37 +63,106 @@ MP_UNITS_EXPORT enum class quantity_character : std::int8_t { scalar, complex, v

namespace detail {

template<typename T, typename U>
concept CommonTypeWith =
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>
concept WeaklyRegular = std::copyable<T> && std::equality_comparable<T>;

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>
concept Scalar = is_scalar<T>;

template<typename T>
concept CastableNumber = CommonTypeWith<T, std::intmax_t> && ScalableNumber<std::common_type_t<T, std::intmax_t>>;
concept Complex = is_complex<T>;

// TODO Fix it according to sudo_cast implementation
template<typename T>
concept Scalable =
CastableNumber<T> || (requires { typename wrapped_type_t<T>; } && CastableNumber<wrapped_type_t<T>> &&
ScalableNumber<T, std::common_type_t<wrapped_type_t<T>, std::intmax_t>>);
concept Vector = is_vector<T>;

template<typename T>
concept WeaklyRegular = std::copyable<T> && std::equality_comparable<T>;
concept Tensor = is_tensor<T>;

template<typename T, quantity_character Ch>
concept IsOfCharacter =
(Ch == quantity_character::scalar && is_scalar<T>) || (Ch == quantity_character::complex && is_complex<T>) ||
(Ch == quantity_character::vector && is_vector<T>) || (Ch == quantity_character::tensor && is_tensor<T>);
;

template<typename T>
using scaling_factor_type_t = conditional<treat_as_floating_point<T>, long double, std::intmax_t>;

template<typename T>
concept ScalarRepresentation = Scalar<T> && WeaklyRegular<T> && requires(T a, T b, scaling_factor_type_t<T> f) {
// scaling
{ a* f } -> Scalar;
{ f* a } -> Scalar;
{ a / f } -> Scalar;

// scalar operations
{ a + b } -> Scalar;
{ a - b } -> Scalar;
{ a* b } -> Scalar;
{ a / b } -> Scalar;
};

template<typename T>
concept ComplexRepresentation = Complex<T> && WeaklyRegular<T> && requires(T a, T b, scaling_factor_type_t<T> f) {
// scaling
// TODO The below conversion to `T` is an exception compared to other representation types
// `std::complex<T>` * `U` do not work, but `std::complex<T>` is convertible from `U`
// Maybe expose this as a customization point?
{ a* T(f) } -> Complex;
{ T(f) * a } -> Complex;
{ a / T(f) } -> Complex;

// complex operations
{ a + b } -> Complex;
{ a - b } -> Complex;
{ a* b } -> Complex;
{ a / b } -> Complex;
// TBD
// { re(a) } -> Scalar;
// { im(a) } -> Scalar;
// { mod(a) } -> Scalar;
// { arg(a) } -> Scalar;
// { conj(a) } -> Complex;
};

// TODO how to check for a complex(Scalar, Scalar) -> Complex?

This comment has been minimized.

Copy link
@JohelEGP

JohelEGP Nov 14, 2024

Collaborator

What's the use case for the library?

This comment has been minimized.

Copy link
@mpusz

mpusz Nov 14, 2024

Author Owner

complex(active_power, reactive_power) should result in the expression template that will be convertible to complex_power

This comment has been minimized.

Copy link
@JohelEGP

JohelEGP Nov 14, 2024

Collaborator

Now, what kind of entity is that complex in complex(active_power, reactive_power)?

This comment has been minimized.

Copy link
@JohelEGP

JohelEGP Nov 14, 2024

Collaborator

Better yet, what's the user code you envision?

This comment has been minimized.

Copy link
@mpusz

mpusz Nov 14, 2024

Author Owner

I am not sure if I understand you.

This complex() is probably a bad idea. However, we need to be able to ensure that the only way to create complex_power is by making a complex quantity from active_power and reactive_power.

This, however, has the same issue as zero_vector below. std::complex and cartesian_vector are not the only types that can be used for those purposes. We need some way to specify a target type of the operation. I still do not know how I will approach that, but I will have to worry about it soon.

This comment has been minimized.

Copy link
@mpusz

mpusz Nov 14, 2024

Author Owner

I think that we need roughly something like this:

quantity a = isq::active_power(60 * W);
quantity r = isq::reactive_power(40 * VA);
quantity q = complex<std::complex<double>>(a, r);
quantity<isq::complex_power[VA], std::complex<double>> c = q;

This comment has been minimized.

Copy link
@JohelEGP

JohelEGP Nov 14, 2024

Collaborator

I see.
So it'd be akin to std::ranges::to,
which can take a template or type.
Now, that's just a customization point template.

This comment has been minimized.

Copy link
@mpusz

mpusz Nov 14, 2024

Author Owner

However, I am not sure how to enable customizations for this, as we can't overload through template parameters. But maybe we do not need any customization, and then this check in line 127 will not be needed.

We will just call a constructor of the provided type with two arguments. So, we should check if T is constructed from two scalars. The problem will be how to get scalars, but we can probably use value_type_t.

This comment has been minimized.

Copy link
@JohelEGP

JohelEGP Nov 14, 2024

Collaborator

Right.
Customization point behavior is only needed
if the type or template can't be constructed from two scalars.

There's no need to get the scalar types.
complex<std::complex<float>>(a, r)
would evaluate to
std::complex<float>(a.numerical-value, r.numerical-value) * isq::complex_power[VA].
complex<std::complex>(a, r)
would evaluate to
std::complex(a.numerical-value, r.numerical-value) * isq::complex_power[VA].


template<typename T>
concept VectorRepresentation = Vector<T> && WeaklyRegular<T> && requires(T a, T b, scaling_factor_type_t<T> f) {
// scaling
{ a* f } -> Vector;
{ f* a } -> Vector;
{ a / f } -> Vector;

// vector operations
{ a + b } -> Vector;
{ a - b } -> Vector;
// TBD
// { norm(a) } -> Scalar;
// { zero_vector<T>() } -> Vector;
// { unit_vector(a) } -> Vector;
// { scalar_product(a, b) } -> Scalar;
// { vector_product(a, b) } -> Vector;
// { tensor_product(a, b) } -> Tensor2;
// divergence(a)
// rotation(a)
};

template<typename T>
concept TensorRepresentation = Tensor<T> && WeaklyRegular<T>; // && requires(T a, T b) {
// TBD
// tensor operations
// { tensor_product(a, b) } -> Tensor4;
// { inner_product(a, b) } -> Tensor2;
// { scalar_product(a, b) } -> Scalar;
//};

} // namespace detail

MP_UNITS_EXPORT template<typename T>
concept Representation =
(is_scalar<T> || is_complex<T> || is_vector<T> || is_tensor<T>) && detail::WeaklyRegular<T> && detail::Scalable<T>;
concept Representation = detail::ScalarRepresentation<T> || detail::ComplexRepresentation<T> ||
detail::VectorRepresentation<T> || detail::TensorRepresentation<T>;

MP_UNITS_EXPORT template<typename T, quantity_character Ch>
concept RepresentationOf =
Representation<T> &&
((Ch == quantity_character::scalar && is_scalar<T>) || (Ch == quantity_character::complex && is_complex<T>) ||
(Ch == quantity_character::vector && is_vector<T>) || (Ch == quantity_character::tensor && is_tensor<T>));
concept RepresentationOf = detail::IsOfCharacter<T, Ch> && Representation<T>;

} // namespace mp_units
6 changes: 3 additions & 3 deletions src/systems/include/mp-units/systems/isq/electromagnetism.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ inline constexpr auto instantaneous_power = electromagnetism_power;
QUANTITY_SPEC(resistance, voltage / electric_current);
QUANTITY_SPEC(conductance, inverse(resistance));
QUANTITY_SPEC(phase_difference, phase_angle);
QUANTITY_SPEC(electric_current_phasor, electric_current);
QUANTITY_SPEC(voltage_phasor, voltage);
QUANTITY_SPEC(electric_current_phasor, electric_current, quantity_character::complex);
QUANTITY_SPEC(voltage_phasor, voltage, quantity_character::complex);
QUANTITY_SPEC(impedance, voltage_phasor / electric_current_phasor);
inline constexpr auto complex_impedance = impedance;
QUANTITY_SPEC(resistance_to_alternating_current, impedance);
Expand All @@ -139,7 +139,7 @@ QUANTITY_SPEC(loss_factor, dimensionless, inverse(quality_factor));
QUANTITY_SPEC(loss_angle, angular_measure);
QUANTITY_SPEC(active_power, isq::power, inverse(period) * (instantaneous_power * time));
QUANTITY_SPEC(complex_power, voltage_phasor* electric_current_phasor); // separate kind
QUANTITY_SPEC(apparent_power, complex_power);
QUANTITY_SPEC(apparent_power, complex_power, quantity_character::scalar);
QUANTITY_SPEC(power_factor, dimensionless, active_power / apparent_power);
QUANTITY_SPEC(reactive_power, isq::mass* pow<2>(isq::length) / pow<3>(isq::time)); // separate kind
QUANTITY_SPEC(non_active_power, pow<1, 2>(pow<2>(apparent_power))); // separate kind
Expand Down
7 changes: 5 additions & 2 deletions test/static/concepts_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ import std;

#if MP_UNITS_HOSTED
template<typename T>
constexpr bool mp_units::is_scalar<std::complex<T>> = true;
constexpr bool mp_units::is_complex<std::complex<T>> = true;
#endif


namespace {

using namespace mp_units;
Expand Down Expand Up @@ -268,7 +269,9 @@ static_assert(Representation<double>);
static_assert(!Representation<bool>);
static_assert(!Representation<std::optional<int>>);
#if MP_UNITS_HOSTED
static_assert(Representation<std::complex<float>>);
static_assert(Representation<std::complex<double>>);
static_assert(Representation<std::complex<long double>>);
static_assert(!Representation<std::string>);
static_assert(!Representation<std::chrono::seconds>);
#endif
Expand All @@ -279,7 +282,7 @@ static_assert(RepresentationOf<double, quantity_character::scalar>);
static_assert(!RepresentationOf<bool, quantity_character::scalar>);
static_assert(!RepresentationOf<std::optional<int>, quantity_character::scalar>);
#if MP_UNITS_HOSTED
static_assert(RepresentationOf<std::complex<double>, quantity_character::scalar>);
static_assert(RepresentationOf<std::complex<double>, quantity_character::complex>);
static_assert(!RepresentationOf<std::chrono::seconds, quantity_character::scalar>);
static_assert(!RepresentationOf<std::string, quantity_character::scalar>);
#endif
Expand Down
6 changes: 3 additions & 3 deletions test/static/isq_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,8 @@ static_assert(verify(isq::instantaneous_power, scalar, W));
static_assert(verify(isq::resistance, scalar, Ω));
static_assert(verify(isq::conductance, scalar, S));
static_assert(verify(isq::phase_difference, scalar, rad));
static_assert(verify(isq::electric_current_phasor, scalar, A));
static_assert(verify(isq::voltage_phasor, scalar, V));
static_assert(verify(isq::electric_current_phasor, complex, A));
static_assert(verify(isq::voltage_phasor, complex, V));
static_assert(verify(isq::impedance, scalar, Ω));
static_assert(verify(isq::complex_impedance, scalar, Ω));
static_assert(verify(isq::resistance_to_alternating_current, scalar, Ω));
Expand All @@ -321,7 +321,7 @@ static_assert(verify(isq::loss_angle, scalar, rad));
static_assert(verify(isq::active_power, scalar, W));
static_assert(verify(isq::apparent_power, scalar, V* A));
static_assert(verify(isq::power_factor, scalar, one));
static_assert(verify(isq::complex_power, scalar, V* A));
static_assert(verify(isq::complex_power, complex, V* A));
static_assert(verify(isq::reactive_power, scalar, V* A));
static_assert(verify(isq::non_active_power, scalar, V* A));
static_assert(verify(isq::active_energy, scalar, J, W* h));
Expand Down
11 changes: 8 additions & 3 deletions test/static/quantity_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <mp-units/bits/hacks.h>
#include <mp-units/ext/fixed_string.h>
#include <mp-units/ext/type_traits.h>
#include <mp-units/systems/isq/electromagnetism.h>
#include <mp-units/systems/isq/mechanics.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si.h>
Expand All @@ -46,7 +47,7 @@ import std;

#if MP_UNITS_HOSTED
template<typename T>
constexpr bool mp_units::is_scalar<std::complex<T>> = true;
constexpr bool mp_units::is_complex<std::complex<T>> = true;
#endif

template<>
Expand Down Expand Up @@ -282,8 +283,12 @@ static_assert((1. * rad + 1. * deg).in(deg) != 0 * deg);

#if MP_UNITS_HOSTED
using namespace std::complex_literals;
static_assert(((2. + 1i) * V).in(mV).numerical_value_in(mV) == 2000. + 1000i);
static_assert(((2. + 1i) * V).in(mV).numerical_value_in(V) == 2. + 1i);
static_assert(((2.f + 1if) * isq::voltage_phasor[V]).in(mV).numerical_value_in(mV) == 2000.f + 1000if);
static_assert(((2.f + 1if) * isq::voltage_phasor[V]).in(mV).numerical_value_in(V) == 2.f + 1if);
static_assert(((2. + 1i) * isq::voltage_phasor[V]).in(mV).numerical_value_in(mV) == 2000. + 1000i);
static_assert(((2. + 1i) * isq::voltage_phasor[V]).in(mV).numerical_value_in(V) == 2. + 1i);
static_assert(((2.L + 1il) * isq::voltage_phasor[V]).in(mV).numerical_value_in(mV) == 2000.L + 1000il);
static_assert(((2.L + 1il) * isq::voltage_phasor[V]).in(mV).numerical_value_in(V) == 2.L + 1il);
#endif

template<template<auto, typename> typename Q>
Expand Down

0 comments on commit c7303cc

Please sign in to comment.