Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion mojito/examples/unit_conversion.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
#include <iostream>
#include <mojito/mojito.hpp>

// Define custom base tags
struct motor_side {};
struct grid_side {};

int main()
{
using namespace mojito;

// --- Standard Unit Conversions ---

// 1. Define base quantities using SI type aliases
voltage_t base_voltage(230.0f);
std::cout << "Base Voltage: " << base_voltage << "\n";
Expand Down Expand Up @@ -35,7 +41,32 @@ int main()

// Conversion works seamlessly with frames
abc<voltage_pu_t> abc_pu = to_pu(abc_si, base_voltage);
std::cout << "abc PU frame:\n " << abc_pu << "\n";
std::cout << "abc PU frame:\n " << abc_pu << "\n\n";

// --- Custom Per-Unit Bases ---
std::cout << "--- Custom Per-Unit Bases ---\n";

// 8. Define custom base quantities
voltage_t motor_base_v(440.0f);
voltage_t grid_base_v(380.0f);

std::cout << "Motor side base: " << motor_base_v << "\n";
std::cout << "Grid side base: " << grid_base_v << "\n";

// 9. Convert SI to custom PU bases
voltage_custom_pu_t<motor_side> v_pu_motor = to_pu<motor_side>(v_si, motor_base_v);
voltage_custom_pu_t<grid_side> v_pu_grid = to_pu<grid_side>(v_si, grid_base_v);

std::cout << "Voltage in Motor-side PU: " << v_pu_motor << "\n";
std::cout << "Voltage in Grid-side PU: " << v_pu_grid << "\n";

// 10. Convert between custom bases (manually through SI)
voltage_custom_pu_t<grid_side> v_grid_from_motor_manual = to_pu<grid_side>(to_si(v_pu_motor, motor_base_v), grid_base_v);
std::cout << "Voltage in Grid-side PU (manual through SI): " << v_grid_from_motor_manual << "\n";

// 11. Convert between custom bases using to_different_pu
voltage_custom_pu_t<grid_side> v_grid_from_motor_direct = to_different_pu<grid_side>(v_pu_motor, motor_base_v, grid_base_v);
std::cout << "Voltage in Grid-side PU (using to_different_pu): " << v_grid_from_motor_direct << "\n";

return 0;
}
7 changes: 7 additions & 0 deletions mojito/include/mojito/pu_conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ constexpr auto to_pu(const dq<quantity<Dim, si>>& si_frame, const divisor<quanti
return dq<result_quantity_t>(to_pu<Base>(si_frame.d(), divisor), to_pu<Base>(si_frame.q(), divisor));
}

template <typename NewBase, typename OldBase, typename Dim, template <typename> class Frame>
constexpr auto to_different_pu(const Frame<quantity<Dim, per_unit<OldBase>>>& pu_frame,
const quantity<Dim, si>& old_base_quantity, const quantity<Dim, si>& new_base_quantity)
{
return to_pu<NewBase>(to_si(pu_frame, old_base_quantity), new_base_quantity);
}

} // namespace mojito

#endif // MOJITO_PU_CONVERSION_HPP
8 changes: 8 additions & 0 deletions mojito/include/mojito/units/conversions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ constexpr auto to_pu(const quantity<Dim, percent>& percent_quantity)
return quantity<Dim, per_unit<Base>>(percent_quantity.value() / real_t{100.0});
}

// Per-Unit <-> Per-Unit
template <typename NewBase, typename OldBase, typename Dim>
constexpr auto to_different_pu(const quantity<Dim, per_unit<OldBase>>& pu_quantity,
const quantity<Dim, si>& old_base_quantity, const quantity<Dim, si>& new_base_quantity)
{
return to_pu<NewBase>(to_si(pu_quantity, old_base_quantity), new_base_quantity);
}

// Casting
template <typename NewType, typename OldDim, typename OldBase>
constexpr auto per_unit_cast(const quantity<OldDim, per_unit<OldBase>>& q)
Expand Down
36 changes: 35 additions & 1 deletion mojito/tests/pu_conversion_test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#include <gtest/gtest.h>

#include <cmath>

#include "mojito/mojito.hpp"


namespace {

using namespace mojito;
Expand All @@ -12,7 +15,7 @@ TEST(PuConversionTest, AbcConversion)
{
voltage_t base_v{real_t{100.0}};
abc<voltage_pu_t> pu_v{voltage_pu_t{real_t{1.0}}, voltage_pu_t{real_t{-0.5}}, voltage_pu_t{real_t{-0.5}}};

auto si_v = to_si(pu_v, base_v);
EXPECT_NEAR(si_v.a().value(), real_t{100.0}, k_epsilon);
EXPECT_NEAR(si_v.b().value(), real_t{-50.0}, k_epsilon);
Expand Down Expand Up @@ -60,4 +63,35 @@ TEST(PuConversionTest, DivisorConversion)
EXPECT_NEAR(pu_v.c().value(), real_t{-0.5}, k_epsilon);
}

TEST(PuConversionTest, ToDifferentPu)
{
struct machine_base {};
struct grid_base {};

voltage_t base_m(100.0);
voltage_t base_g(200.0);

// abc
abc<voltage_custom_pu_t<machine_base>> pu_abc{voltage_custom_pu_t<machine_base>{real_t{1.0}},
voltage_custom_pu_t<machine_base>{real_t{1.0}}, voltage_custom_pu_t<machine_base>{real_t{1.0}}};
auto grid_abc = to_different_pu<grid_base>(pu_abc, base_m, base_g);
EXPECT_NEAR(grid_abc.a().value(), 0.5, k_epsilon);
EXPECT_NEAR(grid_abc.b().value(), 0.5, k_epsilon);
EXPECT_NEAR(grid_abc.c().value(), 0.5, k_epsilon);

// alphabeta
alphabeta<voltage_custom_pu_t<machine_base>> pu_ab{
voltage_custom_pu_t<machine_base>{real_t{1.0}}, voltage_custom_pu_t<machine_base>{real_t{1.0}}};
auto grid_ab = to_different_pu<grid_base>(pu_ab, base_m, base_g);
EXPECT_NEAR(grid_ab.alpha().value(), 0.5, k_epsilon);
EXPECT_NEAR(grid_ab.beta().value(), 0.5, k_epsilon);

// dq
dq<voltage_custom_pu_t<machine_base>> pu_dq{
voltage_custom_pu_t<machine_base>{real_t{1.0}}, voltage_custom_pu_t<machine_base>{real_t{1.0}}};
auto grid_dq = to_different_pu<grid_base>(pu_dq, base_m, base_g);
EXPECT_NEAR(grid_dq.d().value(), 0.5, k_epsilon);
EXPECT_NEAR(grid_dq.q().value(), 0.5, k_epsilon);
}

} // namespace
18 changes: 17 additions & 1 deletion mojito/tests/units_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,23 @@ TEST(UnitsTest, CustomPerUnitBases)

// Casting between PU systems
auto v_m_as_g = per_unit_cast<voltage_custom_pu_t<grid_base>>(v_m);
EXPECT_NEAR(v_m_as_g.value(), 1.0, k_epsilon); // Note: cast just copies value, doesn't re-scale
EXPECT_NEAR(v_m_as_g.value(), real_t{1.0}, k_epsilon); // Note: cast just copies value, doesn't re-scale
}

TEST(UnitsTest, ToDifferentPu)
{
struct machine_base {};
struct grid_base {};

voltage_t base_m(100.0);
voltage_t base_g(200.0);

// 1.0 pu in machine base (100V) should be 0.5 pu in grid base (200V)
auto v_m = voltage_custom_pu_t<machine_base>(1.0);
auto v_g = to_different_pu<grid_base>(v_m, base_m, base_g);

EXPECT_NEAR(v_g.value(), 0.5, k_epsilon);
static_assert(std::is_same_v<decltype(v_g), voltage_custom_pu_t<grid_base>>);
}

TEST(UnitsTest, PercentSystem)
Expand Down
Loading