diff --git a/code_generation/data/attribute_classes/input.json b/code_generation/data/attribute_classes/input.json index 7cf8b5c97..834390925 100644 --- a/code_generation/data/attribute_classes/input.json +++ b/code_generation/data/attribute_classes/input.json @@ -127,6 +127,55 @@ } ] }, + { + "name": "AsymLineInput", + "base": "BranchInput", + "attributes": [ + { + "data_type": "double", + "names": [ + "r_aa", + "r_ba", + "r_bb", + "r_ca", + "r_cb", + "r_cc", + "r_na", + "r_nb", + "r_nc", + "r_nn", + "x_aa", + "x_ba", + "x_bb", + "x_ca", + "x_cb", + "x_cc", + "x_na", + "x_nb", + "x_nc", + "x_nn", + "c_aa", + "c_ba", + "c_bb", + "c_ca", + "c_cb", + "c_cc", + "c_na", + "c_nb", + "c_nc", + "c_nn", + "c0", + "c1" + ], + "description": "Lower triangle matrix values for R, X and C matrices" + }, + { + "data_type": "double", + "names": "i_n", + "description": "rated current" + } + ] + }, { "name": "GenericBranchInput", "base": "BranchInput", diff --git a/code_generation/data/dataset_class_maps/dataset_definitions.json b/code_generation/data/dataset_class_maps/dataset_definitions.json index e4cb9073d..78834d419 100644 --- a/code_generation/data/dataset_class_maps/dataset_definitions.json +++ b/code_generation/data/dataset_class_maps/dataset_definitions.json @@ -12,6 +12,10 @@ "names": ["line"], "class_name": "LineInput" }, + { + "names": ["asym_line"], + "class_name": "AsymLineInput" + }, { "names": ["link"], "class_name": "LinkInput" @@ -67,7 +71,7 @@ "class_name": "NodeOutput" }, { - "names": ["line", "link", "transformer", "generic_branch"], + "names": ["line", "link", "transformer", "generic_branch", "asym_line"], "class_name": "BranchOutput" }, { @@ -108,6 +112,10 @@ "names": ["line"], "class_name": "BranchUpdate" }, + { + "names": ["asym_line"], + "class_name": "BranchUpdate" + }, { "names": ["link"], "class_name": "BranchUpdate" @@ -159,7 +167,7 @@ "class_name": "NodeShortCircuitOutput" }, { - "names": ["line", "link", "transformer"], + "names": ["line", "link", "transformer", "asym_line"], "class_name": "BranchShortCircuitOutput" }, { diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/all_components.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/all_components.hpp index 7ed9be5a3..2293e9dcb 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/all_components.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/all_components.hpp @@ -12,6 +12,7 @@ #include "component/fault.hpp" #include "component/generic_branch.hpp" #include "component/line.hpp" +#include "component/asym_line.hpp" #include "component/link.hpp" #include "component/load_gen.hpp" #include "component/node.hpp" @@ -27,7 +28,7 @@ namespace power_grid_model { using AllComponents = - ComponentList; diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/input.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/input.hpp index 661579d1c..2d1127c88 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/input.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/input.hpp @@ -98,6 +98,55 @@ struct LineInput { operator BranchInput const&() const { return reinterpret_cast(*this); } }; +struct AsymLineInput { + ID id{na_IntID}; // ID of the object + ID from_node{na_IntID}; // node IDs to which this branch is connected at both sides + ID to_node{na_IntID}; // node IDs to which this branch is connected at both sides + IntS from_status{na_IntS}; // whether the branch is connected at each side + IntS to_status{na_IntS}; // whether the branch is connected at each side + double r_aa{nan}; // Lower triangle matrix values for R, X and C matrices + double r_ba{nan}; // Lower triangle matrix values for R, X and C matrices + double r_bb{nan}; // Lower triangle matrix values for R, X and C matrices + double r_ca{nan}; // Lower triangle matrix values for R, X and C matrices + double r_cb{nan}; // Lower triangle matrix values for R, X and C matrices + double r_cc{nan}; // Lower triangle matrix values for R, X and C matrices + double r_na{nan}; // Lower triangle matrix values for R, X and C matrices + double r_nb{nan}; // Lower triangle matrix values for R, X and C matrices + double r_nc{nan}; // Lower triangle matrix values for R, X and C matrices + double r_nn{nan}; // Lower triangle matrix values for R, X and C matrices + double x_aa{nan}; // Lower triangle matrix values for R, X and C matrices + double x_ba{nan}; // Lower triangle matrix values for R, X and C matrices + double x_bb{nan}; // Lower triangle matrix values for R, X and C matrices + double x_ca{nan}; // Lower triangle matrix values for R, X and C matrices + double x_cb{nan}; // Lower triangle matrix values for R, X and C matrices + double x_cc{nan}; // Lower triangle matrix values for R, X and C matrices + double x_na{nan}; // Lower triangle matrix values for R, X and C matrices + double x_nb{nan}; // Lower triangle matrix values for R, X and C matrices + double x_nc{nan}; // Lower triangle matrix values for R, X and C matrices + double x_nn{nan}; // Lower triangle matrix values for R, X and C matrices + double c_aa{nan}; // Lower triangle matrix values for R, X and C matrices + double c_ba{nan}; // Lower triangle matrix values for R, X and C matrices + double c_bb{nan}; // Lower triangle matrix values for R, X and C matrices + double c_ca{nan}; // Lower triangle matrix values for R, X and C matrices + double c_cb{nan}; // Lower triangle matrix values for R, X and C matrices + double c_cc{nan}; // Lower triangle matrix values for R, X and C matrices + double c_na{nan}; // Lower triangle matrix values for R, X and C matrices + double c_nb{nan}; // Lower triangle matrix values for R, X and C matrices + double c_nc{nan}; // Lower triangle matrix values for R, X and C matrices + double c_nn{nan}; // Lower triangle matrix values for R, X and C matrices + double c0{nan}; // Lower triangle matrix values for R, X and C matrices + double c1{nan}; // Lower triangle matrix values for R, X and C matrices + double i_n{nan}; // rated current + + // implicit conversions to BaseInput + operator BaseInput&() { return reinterpret_cast(*this); } + operator BaseInput const&() const { return reinterpret_cast(*this); } + + // implicit conversions to BranchInput + operator BranchInput&() { return reinterpret_cast(*this); } + operator BranchInput const&() const { return reinterpret_cast(*this); } +}; + struct GenericBranchInput { ID id{na_IntID}; // ID of the object ID from_node{na_IntID}; // node IDs to which this branch is connected at both sides diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/meta_gen/input.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/meta_gen/input.hpp index 27deffff7..b96e7f87c 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/meta_gen/input.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/meta_gen/input.hpp @@ -111,6 +111,52 @@ struct get_attributes_list { }; }; +template<> +struct get_attributes_list { + static constexpr std::array value{ + // all attributes including base class + + meta_data_gen::get_meta_attribute<&AsymLineInput::id>(offsetof(AsymLineInput, id), "id"), + meta_data_gen::get_meta_attribute<&AsymLineInput::from_node>(offsetof(AsymLineInput, from_node), "from_node"), + meta_data_gen::get_meta_attribute<&AsymLineInput::to_node>(offsetof(AsymLineInput, to_node), "to_node"), + meta_data_gen::get_meta_attribute<&AsymLineInput::from_status>(offsetof(AsymLineInput, from_status), "from_status"), + meta_data_gen::get_meta_attribute<&AsymLineInput::to_status>(offsetof(AsymLineInput, to_status), "to_status"), + meta_data_gen::get_meta_attribute<&AsymLineInput::r_aa>(offsetof(AsymLineInput, r_aa), "r_aa"), + meta_data_gen::get_meta_attribute<&AsymLineInput::r_ba>(offsetof(AsymLineInput, r_ba), "r_ba"), + meta_data_gen::get_meta_attribute<&AsymLineInput::r_bb>(offsetof(AsymLineInput, r_bb), "r_bb"), + meta_data_gen::get_meta_attribute<&AsymLineInput::r_ca>(offsetof(AsymLineInput, r_ca), "r_ca"), + meta_data_gen::get_meta_attribute<&AsymLineInput::r_cb>(offsetof(AsymLineInput, r_cb), "r_cb"), + meta_data_gen::get_meta_attribute<&AsymLineInput::r_cc>(offsetof(AsymLineInput, r_cc), "r_cc"), + meta_data_gen::get_meta_attribute<&AsymLineInput::r_na>(offsetof(AsymLineInput, r_na), "r_na"), + meta_data_gen::get_meta_attribute<&AsymLineInput::r_nb>(offsetof(AsymLineInput, r_nb), "r_nb"), + meta_data_gen::get_meta_attribute<&AsymLineInput::r_nc>(offsetof(AsymLineInput, r_nc), "r_nc"), + meta_data_gen::get_meta_attribute<&AsymLineInput::r_nn>(offsetof(AsymLineInput, r_nn), "r_nn"), + meta_data_gen::get_meta_attribute<&AsymLineInput::x_aa>(offsetof(AsymLineInput, x_aa), "x_aa"), + meta_data_gen::get_meta_attribute<&AsymLineInput::x_ba>(offsetof(AsymLineInput, x_ba), "x_ba"), + meta_data_gen::get_meta_attribute<&AsymLineInput::x_bb>(offsetof(AsymLineInput, x_bb), "x_bb"), + meta_data_gen::get_meta_attribute<&AsymLineInput::x_ca>(offsetof(AsymLineInput, x_ca), "x_ca"), + meta_data_gen::get_meta_attribute<&AsymLineInput::x_cb>(offsetof(AsymLineInput, x_cb), "x_cb"), + meta_data_gen::get_meta_attribute<&AsymLineInput::x_cc>(offsetof(AsymLineInput, x_cc), "x_cc"), + meta_data_gen::get_meta_attribute<&AsymLineInput::x_na>(offsetof(AsymLineInput, x_na), "x_na"), + meta_data_gen::get_meta_attribute<&AsymLineInput::x_nb>(offsetof(AsymLineInput, x_nb), "x_nb"), + meta_data_gen::get_meta_attribute<&AsymLineInput::x_nc>(offsetof(AsymLineInput, x_nc), "x_nc"), + meta_data_gen::get_meta_attribute<&AsymLineInput::x_nn>(offsetof(AsymLineInput, x_nn), "x_nn"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c_aa>(offsetof(AsymLineInput, c_aa), "c_aa"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c_ba>(offsetof(AsymLineInput, c_ba), "c_ba"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c_bb>(offsetof(AsymLineInput, c_bb), "c_bb"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c_ca>(offsetof(AsymLineInput, c_ca), "c_ca"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c_cb>(offsetof(AsymLineInput, c_cb), "c_cb"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c_cc>(offsetof(AsymLineInput, c_cc), "c_cc"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c_na>(offsetof(AsymLineInput, c_na), "c_na"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c_nb>(offsetof(AsymLineInput, c_nb), "c_nb"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c_nc>(offsetof(AsymLineInput, c_nc), "c_nc"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c_nn>(offsetof(AsymLineInput, c_nn), "c_nn"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c0>(offsetof(AsymLineInput, c0), "c0"), + meta_data_gen::get_meta_attribute<&AsymLineInput::c1>(offsetof(AsymLineInput, c1), "c1"), + meta_data_gen::get_meta_attribute<&AsymLineInput::i_n>(offsetof(AsymLineInput, i_n), "i_n"), + }; +}; + template<> struct get_attributes_list { static constexpr std::array value{ diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/static_asserts/input.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/static_asserts/input.hpp index ee0052125..76913f717 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/static_asserts/input.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/static_asserts/input.hpp @@ -70,6 +70,25 @@ static_assert(offsetof(LineInput, to_node) == offsetof(BranchInput, to_node)); static_assert(offsetof(LineInput, from_status) == offsetof(BranchInput, from_status)); static_assert(offsetof(LineInput, to_status) == offsetof(BranchInput, to_status)); +// static asserts for AsymLineInput +static_assert(std::is_standard_layout_v); +// static asserts for conversion of AsymLineInput to BaseInput +static_assert(std::alignment_of_v >= std::alignment_of_v); +static_assert(std::same_as); +static_assert(offsetof(AsymLineInput, id) == offsetof(BaseInput, id)); +// static asserts for conversion of AsymLineInput to BranchInput +static_assert(std::alignment_of_v >= std::alignment_of_v); +static_assert(std::same_as); +static_assert(std::same_as); +static_assert(std::same_as); +static_assert(std::same_as); +static_assert(std::same_as); +static_assert(offsetof(AsymLineInput, id) == offsetof(BranchInput, id)); +static_assert(offsetof(AsymLineInput, from_node) == offsetof(BranchInput, from_node)); +static_assert(offsetof(AsymLineInput, to_node) == offsetof(BranchInput, to_node)); +static_assert(offsetof(AsymLineInput, from_status) == offsetof(BranchInput, from_status)); +static_assert(offsetof(AsymLineInput, to_status) == offsetof(BranchInput, to_status)); + // static asserts for GenericBranchInput static_assert(std::is_standard_layout_v); // static asserts for conversion of GenericBranchInput to BaseInput diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/common/matrix_utils.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/common/matrix_utils.hpp new file mode 100644 index 000000000..1ab56abe3 --- /dev/null +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/common/matrix_utils.hpp @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +#pragma once + +#include "three_phase_tensor.hpp" + +namespace power_grid_model { + +inline DoubleComplex average_of_diagonal_of_matrix(const ComplexTensor &matrix) { + return (matrix(0,0) + matrix(1,1) + matrix(2,2)) / 3.0; +} + +inline DoubleComplex average_of_off_diagonal_of_matrix(const ComplexTensor &matrix) { + return (matrix(0,1) + matrix(1,2) + matrix(1,0) + matrix(1,2) + matrix(2,0) + matrix(2,1)) / 6.0; +} + +} // namespace power_grid_model diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/common/three_phase_tensor.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/common/three_phase_tensor.hpp index afd72324e..ec5433c4b 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/common/three_phase_tensor.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/common/three_phase_tensor.hpp @@ -22,6 +22,7 @@ namespace three_phase_tensor { template using Eigen3Vector = Eigen::Array; template using Eigen3Tensor = Eigen::Array; +template using Eigen4Tensor = Eigen::Array; template using Eigen3DiagonalTensor = Eigen::DiagonalMatrix; template class Vector : public Eigen3Vector { @@ -61,6 +62,7 @@ template class Tensor : public Eigen3Tensor { // additional constructors explicit Tensor(T const& x) { (*this) << x, 0.0, 0.0, 0.0, x, 0.0, 0.0, 0.0, x; } explicit Tensor(T const& s, T const& m) { (*this) << s, m, m, m, s, m, m, m, s; } + explicit Tensor(T const& s1, T const& s2, T const& s3, T const& m12, T const& m13, T const& m23) { (*this) << s1, m12, m13, m12, s2, m23, m13, m23, s3; } explicit Tensor(Vector const& v) { (*this) << v(0), 0.0, 0.0, 0.0, v(1), 0.0, 0.0, 0.0, v(2); } // eigen expression template Tensor(Eigen::ArrayBase const& other) : Eigen3Tensor{other} {} @@ -70,6 +72,22 @@ template class Tensor : public Eigen3Tensor { } }; +template class Tensor4 : public Eigen4Tensor { + public: + Tensor4() { (*this) = Eigen4Tensor::Zero(); } + // additional constructors + explicit Tensor4(T const& x) { (*this) << x, 0.0, 0.0, 0.0, 0.0, x, 0.0, 0.0, 0.0, 0.0, x, 0.0, 0.0, 0.0, 0.0, x; } + explicit Tensor4(T const& s, T const& m) { (*this) << s, m, m, m, m, s, m, m, m, m, s, m, m, m, m, s; } + explicit Tensor4(T const& s1, T const& s2, T const& s3, T const& s4, T const& m12, T const& m13, T const& m14, T const& m23, T const& m24, T const& m34) { (*this) << s1, m12, m13, m14, m12, s2, m23, m24, m13, m23, s3, m34, m14, m24, m34, s4; } + explicit Tensor4(Vector const& v) { (*this) << v(0), 0.0, 0.0, 0.0, 0.0, v(1), 0.0, 0.0, 0.0, 0.0, v(2), 0.0, 0.0, 0.0, 0.0, v(3); } + // eigen expression + template Tensor4(Eigen::ArrayBase const& other) : Eigen4Tensor{other} {} + template Tensor4& operator=(Eigen::ArrayBase const& other) { + this->Eigen4Tensor::operator=(other); + return *this; + } +}; + template class DiagonalTensor : public Eigen3DiagonalTensor { public: DiagonalTensor() { (*this).setZero(); } @@ -92,6 +110,7 @@ template using RealTensor = std::conditional_t, double, three_phase_tensor::Tensor>; template using ComplexTensor = std::conditional_t, DoubleComplex, three_phase_tensor::Tensor>; +using ComplexTensor4 = three_phase_tensor::Tensor4; template using RealDiagonalTensor = std::conditional_t, double, three_phase_tensor::DiagonalTensor>; diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/component/asym_line.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/component/asym_line.hpp new file mode 100644 index 000000000..4722ccfb9 --- /dev/null +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/component/asym_line.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include + +#include +#include "branch.hpp" + +#include "../auxiliary/input.hpp" +#include "../auxiliary/output.hpp" +#include "../auxiliary/update.hpp" +#include "../calculation_parameters.hpp" +#include "../common/common.hpp" +#include "../common/three_phase_tensor.hpp" +#include "../common/matrix_utils.hpp" +#include "line_utils.hpp" + +namespace power_grid_model { + +class AsymLine : public Branch { + public: + using InputType = AsymLineInput; + using UpdateType = BranchUpdate; + static constexpr char const* name = "asym_line"; + + explicit AsymLine(AsymLineInput const& asym_line_input, double system_frequency, double u1, double u2) + : Branch{asym_line_input}, i_n_{asym_line_input.i_n}, base_i_{base_power_3p / u1 / sqrt3} { + if (cabs(u1 - u2) > numerical_tolerance) { + throw ConflictVoltage{id(), from_node(), to_node(), u1, u2}; + } + + ComplexTensor c_matrix = compute_c_matrix_from_input(asym_line_input); + ComplexTensor z_series = compute_z_series_from_input(asym_line_input); + + double const base_y = base_i_ / (u1 / sqrt3); + + y_series_abc_ = 1 / base_y * inv(z_series); + y_shunt_abc_ = 1 / base_y * (2 * pi * system_frequency * c_matrix * 1.0i); + + } + + // override getter + constexpr double base_i_from() const override { return base_i_; } + constexpr double base_i_to() const override { return base_i_; } + constexpr double loading(double /* max_s */, double max_i) const override { return max_i / i_n_; }; + constexpr double phase_shift() const override { return 0.0; } + constexpr bool is_param_mutable() const override { return false; } + + private: + double i_n_; + double base_i_; + ComplexTensor y_series_abc_; + ComplexTensor y_shunt_abc_; + + ComplexTensor compute_z_series_from_input(const power_grid_model::AsymLineInput& asym_line_input) { + ComplexTensor z_series_abc; + if (is_nan(asym_line_input.r_na) && is_nan(asym_line_input.x_na)) { + ComplexTensor r_matrix = ComplexTensor(asym_line_input.r_aa, asym_line_input.r_bb, asym_line_input.r_cc, asym_line_input.r_ba, asym_line_input.r_ca, asym_line_input.r_cb); + ComplexTensor x_matrix = ComplexTensor(asym_line_input.x_aa, asym_line_input.x_bb, asym_line_input.x_cc, asym_line_input.x_ba, asym_line_input.x_ca, asym_line_input.x_cb); + z_series_abc = r_matrix + 1.0i * x_matrix; + } + else { + ComplexTensor4 r_matrix = ComplexTensor4(asym_line_input.r_aa, asym_line_input.r_bb, asym_line_input.r_cc, asym_line_input.r_nn, asym_line_input.r_ba, asym_line_input.r_ca, asym_line_input.r_na, asym_line_input.r_cb, asym_line_input.r_nb, asym_line_input.r_nc); + ComplexTensor4 x_matrix = ComplexTensor4(asym_line_input.x_aa, asym_line_input.x_bb, asym_line_input.x_cc, asym_line_input.x_nn, asym_line_input.x_ba, asym_line_input.x_ca, asym_line_input.x_na, asym_line_input.x_cb, asym_line_input.x_nb, asym_line_input.x_nc); + ComplexTensor4 z = r_matrix + 1.0i * x_matrix; + z_series_abc = kron_reduction(z); + } + return z_series_abc; + } + + ComplexTensor compute_c_matrix_from_input(const power_grid_model::AsymLineInput& asym_line_input) { + ComplexTensor c_matrix; + if (!is_nan(asym_line_input.c0) && !is_nan(asym_line_input.c1)) { + c_matrix = ComplexTensor{(2.0 * asym_line_input.c1 + asym_line_input.c0) / 3.0, (asym_line_input.c0 - asym_line_input.c1) / 3.0 }; + } + else if (is_nan(asym_line_input.c_nn)) { + c_matrix = ComplexTensor(asym_line_input.c_aa, asym_line_input.c_bb, asym_line_input.c_cc, asym_line_input.c_ba, asym_line_input.c_ca, asym_line_input.c_cb); + } + else { + ComplexTensor4 c_matrix_neutral = ComplexTensor4(asym_line_input.c_aa, asym_line_input.c_bb, asym_line_input.c_cc, asym_line_input.c_nn, asym_line_input.c_ba, asym_line_input.c_ca, asym_line_input.c_na, asym_line_input.c_cb, asym_line_input.c_nb, asym_line_input.c_nc); + c_matrix = kron_reduction(c_matrix_neutral); + } + return c_matrix; + } + + BranchCalcParam sym_calc_param() const override final { + DoubleComplex y1_series = average_of_diagonal_of_matrix(y_series_abc_) - average_of_off_diagonal_of_matrix(y_series_abc_); + DoubleComplex y1_shunt = average_of_diagonal_of_matrix(y_shunt_abc_) - average_of_off_diagonal_of_matrix(y_shunt_abc_); + return calc_param_y_sym(y1_series, y1_shunt, 1.0); + } + + BranchCalcParam asym_calc_param() const override final { + BranchCalcParam param{}; + // not both connected + if (!branch_status()) { + // single connected + if (from_status() || to_status()) { + // branch_shunt = 0.5 * y_shunt + 1.0 / (1.0 / y_series + 2.0 / y_shunt); + ComplexTensor branch_shunt = ComplexTensor(); + if ((cabs(y_shunt_abc_) >= numerical_tolerance).all()) { + branch_shunt = 0.5 * inv(y_shunt_abc_) + inv(inv(y_series_abc_) + 2.0 * inv(y_shunt_abc_)); + } + // from or to connected + param.yff() = from_status() ? branch_shunt : ComplexTensor(); + param.ytt() = to_status() ? branch_shunt : ComplexTensor(); + } + } + // both connected + else { + param.ytt() = y_series_abc_ + 0.5 * y_shunt_abc_; + param.yff() = param.ytt(); + param.yft() = -y_series_abc_; + param.ytf() = -y_series_abc_; + } + return param; + } +}; +} diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/component/line_utils.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/component/line_utils.hpp new file mode 100644 index 000000000..3c107fb06 --- /dev/null +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/component/line_utils.hpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +#pragma once + +#include "../common/three_phase_tensor.hpp" + +namespace power_grid_model { + +inline ComplexTensor kron_reduction(const ComplexTensor4& matrix_to_reduce) { + ComplexTensor4 Y = matrix_to_reduce; + ComplexTensor Y_aa = ComplexTensor(Y(0,0), Y(1,1), Y(2, 2), Y(1,0), Y(2,0), Y(2,1)); + ComplexValue Y_ab(Y(0,3), Y(1,3), Y(2,3)); + ComplexValue Y_ba(Y(3,0), Y(3,1), Y(3,2)); + DoubleComplex Y_bb_inv = 1.0 / Y(3,3); + return Y_aa - vector_outer_product(Y_ba, Y_ab) * Y_bb_inv; +} + +} // namespace power_grid_model diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/input.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/input.hpp index 67a2124fb..c20443e77 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/input.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/input.hpp @@ -42,7 +42,7 @@ inline void add_component(MainModelState& state, ForwardIter double const u1 = get_component(state, input.from_node).u_rated(); double const u2 = get_component(state, input.to_node).u_rated(); // set system frequency for line - if constexpr (std::same_as) { + if constexpr (std::same_as || std::same_as) { emplace_component(state, id, input, system_frequency, u1, u2); } else { emplace_component(state, id, input, u1, u2); diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset_definitions.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset_definitions.h index 3be340891..2c47dc4fa 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset_definitions.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset_definitions.h @@ -47,6 +47,47 @@ PGM_API extern PGM_MetaAttribute const* const PGM_def_input_line_x0; PGM_API extern PGM_MetaAttribute const* const PGM_def_input_line_c0; PGM_API extern PGM_MetaAttribute const* const PGM_def_input_line_tan0; PGM_API extern PGM_MetaAttribute const* const PGM_def_input_line_i_n; +// component asym_line +PGM_API extern PGM_MetaComponent const* const PGM_def_input_asym_line; +// attributes of input asym_line +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_id; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_from_node; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_to_node; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_from_status; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_to_status; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_r_aa; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_r_ba; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_r_bb; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_r_ca; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_r_cb; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_r_cc; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_r_na; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_r_nb; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_r_nc; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_r_nn; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_x_aa; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_x_ba; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_x_bb; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_x_ca; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_x_cb; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_x_cc; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_x_na; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_x_nb; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_x_nc; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_x_nn; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c_aa; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c_ba; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c_bb; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c_ca; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c_cb; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c_cc; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c_na; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c_nb; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c_nc; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c_nn; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c0; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_c1; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_asym_line_i_n; // component link PGM_API extern PGM_MetaComponent const* const PGM_def_input_link; // attributes of input link @@ -340,6 +381,20 @@ PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_ PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_q_to; PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_i_to; PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_s_to; +// component asym_line +PGM_API extern PGM_MetaComponent const* const PGM_def_sym_output_asym_line; +// attributes of sym_output asym_line +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_id; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_energized; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_loading; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_p_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_q_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_i_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_s_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_p_to; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_q_to; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_i_to; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_s_to; // component transformer_tap_regulator PGM_API extern PGM_MetaComponent const* const PGM_def_sym_output_transformer_tap_regulator; // attributes of sym_output transformer_tap_regulator @@ -526,6 +581,20 @@ PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_q_to; PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_i_to; PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_s_to; +// component asym_line +PGM_API extern PGM_MetaComponent const* const PGM_def_asym_output_asym_line; +// attributes of asym_output asym_line +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_id; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_energized; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_loading; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_p_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_q_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_i_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_s_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_p_to; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_q_to; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_i_to; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_s_to; // component transformer_tap_regulator PGM_API extern PGM_MetaComponent const* const PGM_def_asym_output_transformer_tap_regulator; // attributes of asym_output transformer_tap_regulator @@ -656,6 +725,12 @@ PGM_API extern PGM_MetaComponent const* const PGM_def_update_line; PGM_API extern PGM_MetaAttribute const* const PGM_def_update_line_id; PGM_API extern PGM_MetaAttribute const* const PGM_def_update_line_from_status; PGM_API extern PGM_MetaAttribute const* const PGM_def_update_line_to_status; +// component asym_line +PGM_API extern PGM_MetaComponent const* const PGM_def_update_asym_line; +// attributes of update asym_line +PGM_API extern PGM_MetaAttribute const* const PGM_def_update_asym_line_id; +PGM_API extern PGM_MetaAttribute const* const PGM_def_update_asym_line_from_status; +PGM_API extern PGM_MetaAttribute const* const PGM_def_update_asym_line_to_status; // component link PGM_API extern PGM_MetaComponent const* const PGM_def_update_link; // attributes of update link @@ -806,6 +881,15 @@ PGM_API extern PGM_MetaAttribute const* const PGM_def_sc_output_transformer_i_fr PGM_API extern PGM_MetaAttribute const* const PGM_def_sc_output_transformer_i_from_angle; PGM_API extern PGM_MetaAttribute const* const PGM_def_sc_output_transformer_i_to; PGM_API extern PGM_MetaAttribute const* const PGM_def_sc_output_transformer_i_to_angle; +// component asym_line +PGM_API extern PGM_MetaComponent const* const PGM_def_sc_output_asym_line; +// attributes of sc_output asym_line +PGM_API extern PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_id; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_energized; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_i_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_i_from_angle; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_i_to; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_i_to_angle; // component three_winding_transformer PGM_API extern PGM_MetaComponent const* const PGM_def_sc_output_three_winding_transformer; // attributes of sc_output three_winding_transformer diff --git a/power_grid_model_c/power_grid_model_c/src/dataset_definitions.cpp b/power_grid_model_c/power_grid_model_c/src/dataset_definitions.cpp index 9639a9755..c7d309962 100644 --- a/power_grid_model_c/power_grid_model_c/src/dataset_definitions.cpp +++ b/power_grid_model_c/power_grid_model_c/src/dataset_definitions.cpp @@ -36,6 +36,47 @@ PGM_MetaAttribute const* const PGM_def_input_line_x0 = PGM_meta_get_attribute_by PGM_MetaAttribute const* const PGM_def_input_line_c0 = PGM_meta_get_attribute_by_name(nullptr, "input", "line", "c0"); PGM_MetaAttribute const* const PGM_def_input_line_tan0 = PGM_meta_get_attribute_by_name(nullptr, "input", "line", "tan0"); PGM_MetaAttribute const* const PGM_def_input_line_i_n = PGM_meta_get_attribute_by_name(nullptr, "input", "line", "i_n"); +// component asym_line +PGM_MetaComponent const* const PGM_def_input_asym_line = PGM_meta_get_component_by_name(nullptr, "input", "asym_line"); +// attributes of input asym_line +PGM_MetaAttribute const* const PGM_def_input_asym_line_id = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "id"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_from_node = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "from_node"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_to_node = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "to_node"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_from_status = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "from_status"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_to_status = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "to_status"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_r_aa = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "r_aa"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_r_ba = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "r_ba"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_r_bb = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "r_bb"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_r_ca = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "r_ca"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_r_cb = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "r_cb"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_r_cc = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "r_cc"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_r_na = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "r_na"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_r_nb = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "r_nb"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_r_nc = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "r_nc"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_r_nn = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "r_nn"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_x_aa = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "x_aa"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_x_ba = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "x_ba"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_x_bb = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "x_bb"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_x_ca = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "x_ca"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_x_cb = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "x_cb"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_x_cc = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "x_cc"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_x_na = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "x_na"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_x_nb = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "x_nb"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_x_nc = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "x_nc"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_x_nn = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "x_nn"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c_aa = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c_aa"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c_ba = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c_ba"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c_bb = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c_bb"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c_ca = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c_ca"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c_cb = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c_cb"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c_cc = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c_cc"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c_na = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c_na"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c_nb = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c_nb"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c_nc = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c_nc"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c_nn = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c_nn"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c0 = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c0"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_c1 = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "c1"); +PGM_MetaAttribute const* const PGM_def_input_asym_line_i_n = PGM_meta_get_attribute_by_name(nullptr, "input", "asym_line", "i_n"); // component link PGM_MetaComponent const* const PGM_def_input_link = PGM_meta_get_component_by_name(nullptr, "input", "link"); // attributes of input link @@ -329,6 +370,20 @@ PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_p_to = PGM_meta PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_q_to = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "generic_branch", "q_to"); PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_i_to = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "generic_branch", "i_to"); PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_s_to = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "generic_branch", "s_to"); +// component asym_line +PGM_MetaComponent const* const PGM_def_sym_output_asym_line = PGM_meta_get_component_by_name(nullptr, "sym_output", "asym_line"); +// attributes of sym_output asym_line +PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_id = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "asym_line", "id"); +PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_energized = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "asym_line", "energized"); +PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_loading = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "asym_line", "loading"); +PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_p_from = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "asym_line", "p_from"); +PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_q_from = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "asym_line", "q_from"); +PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_i_from = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "asym_line", "i_from"); +PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_s_from = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "asym_line", "s_from"); +PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_p_to = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "asym_line", "p_to"); +PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_q_to = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "asym_line", "q_to"); +PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_i_to = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "asym_line", "i_to"); +PGM_MetaAttribute const* const PGM_def_sym_output_asym_line_s_to = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "asym_line", "s_to"); // component transformer_tap_regulator PGM_MetaComponent const* const PGM_def_sym_output_transformer_tap_regulator = PGM_meta_get_component_by_name(nullptr, "sym_output", "transformer_tap_regulator"); // attributes of sym_output transformer_tap_regulator @@ -515,6 +570,20 @@ PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_p_to = PGM_met PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_q_to = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "generic_branch", "q_to"); PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_i_to = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "generic_branch", "i_to"); PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_s_to = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "generic_branch", "s_to"); +// component asym_line +PGM_MetaComponent const* const PGM_def_asym_output_asym_line = PGM_meta_get_component_by_name(nullptr, "asym_output", "asym_line"); +// attributes of asym_output asym_line +PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_id = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "asym_line", "id"); +PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_energized = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "asym_line", "energized"); +PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_loading = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "asym_line", "loading"); +PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_p_from = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "asym_line", "p_from"); +PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_q_from = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "asym_line", "q_from"); +PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_i_from = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "asym_line", "i_from"); +PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_s_from = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "asym_line", "s_from"); +PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_p_to = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "asym_line", "p_to"); +PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_q_to = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "asym_line", "q_to"); +PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_i_to = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "asym_line", "i_to"); +PGM_MetaAttribute const* const PGM_def_asym_output_asym_line_s_to = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "asym_line", "s_to"); // component transformer_tap_regulator PGM_MetaComponent const* const PGM_def_asym_output_transformer_tap_regulator = PGM_meta_get_component_by_name(nullptr, "asym_output", "transformer_tap_regulator"); // attributes of asym_output transformer_tap_regulator @@ -645,6 +714,12 @@ PGM_MetaComponent const* const PGM_def_update_line = PGM_meta_get_component_by_n PGM_MetaAttribute const* const PGM_def_update_line_id = PGM_meta_get_attribute_by_name(nullptr, "update", "line", "id"); PGM_MetaAttribute const* const PGM_def_update_line_from_status = PGM_meta_get_attribute_by_name(nullptr, "update", "line", "from_status"); PGM_MetaAttribute const* const PGM_def_update_line_to_status = PGM_meta_get_attribute_by_name(nullptr, "update", "line", "to_status"); +// component asym_line +PGM_MetaComponent const* const PGM_def_update_asym_line = PGM_meta_get_component_by_name(nullptr, "update", "asym_line"); +// attributes of update asym_line +PGM_MetaAttribute const* const PGM_def_update_asym_line_id = PGM_meta_get_attribute_by_name(nullptr, "update", "asym_line", "id"); +PGM_MetaAttribute const* const PGM_def_update_asym_line_from_status = PGM_meta_get_attribute_by_name(nullptr, "update", "asym_line", "from_status"); +PGM_MetaAttribute const* const PGM_def_update_asym_line_to_status = PGM_meta_get_attribute_by_name(nullptr, "update", "asym_line", "to_status"); // component link PGM_MetaComponent const* const PGM_def_update_link = PGM_meta_get_component_by_name(nullptr, "update", "link"); // attributes of update link @@ -795,6 +870,15 @@ PGM_MetaAttribute const* const PGM_def_sc_output_transformer_i_from = PGM_meta_g PGM_MetaAttribute const* const PGM_def_sc_output_transformer_i_from_angle = PGM_meta_get_attribute_by_name(nullptr, "sc_output", "transformer", "i_from_angle"); PGM_MetaAttribute const* const PGM_def_sc_output_transformer_i_to = PGM_meta_get_attribute_by_name(nullptr, "sc_output", "transformer", "i_to"); PGM_MetaAttribute const* const PGM_def_sc_output_transformer_i_to_angle = PGM_meta_get_attribute_by_name(nullptr, "sc_output", "transformer", "i_to_angle"); +// component asym_line +PGM_MetaComponent const* const PGM_def_sc_output_asym_line = PGM_meta_get_component_by_name(nullptr, "sc_output", "asym_line"); +// attributes of sc_output asym_line +PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_id = PGM_meta_get_attribute_by_name(nullptr, "sc_output", "asym_line", "id"); +PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_energized = PGM_meta_get_attribute_by_name(nullptr, "sc_output", "asym_line", "energized"); +PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_i_from = PGM_meta_get_attribute_by_name(nullptr, "sc_output", "asym_line", "i_from"); +PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_i_from_angle = PGM_meta_get_attribute_by_name(nullptr, "sc_output", "asym_line", "i_from_angle"); +PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_i_to = PGM_meta_get_attribute_by_name(nullptr, "sc_output", "asym_line", "i_to"); +PGM_MetaAttribute const* const PGM_def_sc_output_asym_line_i_to_angle = PGM_meta_get_attribute_by_name(nullptr, "sc_output", "asym_line", "i_to_angle"); // component three_winding_transformer PGM_MetaComponent const* const PGM_def_sc_output_three_winding_transformer = PGM_meta_get_component_by_name(nullptr, "sc_output", "three_winding_transformer"); // attributes of sc_output three_winding_transformer diff --git a/src/power_grid_model/_core/dataset_definitions.py b/src/power_grid_model/_core/dataset_definitions.py index d8f8031b8..623aa46a8 100644 --- a/src/power_grid_model/_core/dataset_definitions.py +++ b/src/power_grid_model/_core/dataset_definitions.py @@ -56,6 +56,7 @@ class ComponentType(str, Enum, metaclass=_MetaEnum): node = "node" line = "line" + asym_line = "asym_line" link = "link" generic_branch = "generic_branch" transformer = "transformer" diff --git a/src/power_grid_model/validation/validation.py b/src/power_grid_model/validation/validation.py index 69baa895f..6cee02866 100644 --- a/src/power_grid_model/validation/validation.py +++ b/src/power_grid_model/validation/validation.py @@ -307,6 +307,7 @@ def validate_required_values( required["branch"] = required["base"] + ["from_node", "to_node", "from_status", "to_status"] required["link"] = required["branch"].copy() required["line"] = required["branch"] + ["r1", "x1", "c1", "tan1"] + required["asym_line"] = required["branch"] + ["r_aa", "r_ba", "r_bb", "r_ca", "r_cb", "r_cc", "x_aa", "x_ba", "x_bb", "x_ca", "x_cb", "x_cc"] required["transformer"] = required["branch"] + [ "u1", "u2", @@ -477,6 +478,7 @@ def validate_values(data: SingleDataset, calculation_type: CalculationType | Non component_validators = { "node": validate_node, "line": validate_line, + "asym_line": validate_asym_line, "link": lambda d: validate_branch(d, ComponentType.link), "generic_branch": validate_generic_branch, "transformer": validate_transformer, @@ -544,6 +546,41 @@ def validate_line(data: SingleDataset) -> list[ValidationError]: return errors +def validate_asym_line(data : SingleDataset) -> list[ValidationError]: + errors = validate_branch(data, ComponentType.asym_line) + errors += _all_greater_than_zero(data, ComponentType.asym_line, "i_n") + required_fields = ["r_aa", "r_ba", "r_bb", "r_ca", "r_cb", "r_cc", "x_aa", "x_ba", "x_bb", "x_ca", "x_cb", "x_cc"] + optional_fields = ["r_na", "r_nb", "r_nc", "r_nn", "x_na", "x_nb", "x_nc", "x_nn"] + required_c_matrix_fields = ["c_aa", "c_ba", "c_bb", "c_ca", "c_cb", "c_cc"] + optional_c_matrix_fields = ["c_na", "c_nb", "c_nc", "c_nn"] + c_fields = ["c0", "c1"] + for field in required_fields + optional_fields + required_c_matrix_fields + c_fields + optional_c_matrix_fields: + errors += _all_greater_than_zero(data, ComponentType.asym_line, field) + + for i_data_record in range(0,len(data[ComponentType.asym_line])): + new_errors = _none_missing(data, ComponentType.asym_line, optional_fields, i_data_record) + if 0 < len(new_errors) < len(optional_fields): + errors += new_errors + + new_errors_c_matrix_req = _none_missing(data, ComponentType.asym_line, required_c_matrix_fields, i_data_record) + if 0 < len(new_errors_c_matrix_req) < len(new_errors_c_matrix_req): + errors += new_errors_c_matrix_req + + new_errors_c_matrix_opt = _none_missing(data, ComponentType.asym_line, optional_c_matrix_fields, i_data_record) + if 0 < len(new_errors_c_matrix_opt) < len(new_errors_c_matrix_opt): + errors += new_errors_c_matrix_opt + + new_errors_c1_c0 = _none_missing(data, ComponentType.asym_line, c_fields, i_data_record) + if 0 < len(new_errors_c1_c0) < len(c_fields): + errors += new_errors_c1_c0 + + if len(required_c_matrix_fields) == len(new_errors_c_matrix_req) and len(new_errors_c1_c0) == len(c_fields): + errors += new_errors_c_matrix_req + errors += new_errors_c1_c0 + + return errors + + def validate_generic_branch(data: SingleDataset) -> list[ValidationError]: errors = validate_branch(data, ComponentType.generic_branch) errors += _all_greater_than_zero(data, ComponentType.generic_branch, "k") diff --git a/tests/cpp_unit_tests/CMakeLists.txt b/tests/cpp_unit_tests/CMakeLists.txt index 12c4d801a..44550c2b1 100644 --- a/tests/cpp_unit_tests/CMakeLists.txt +++ b/tests/cpp_unit_tests/CMakeLists.txt @@ -13,6 +13,7 @@ set(PROJECT_SOURCES "test_component_update.cpp" "test_three_phase_tensor.cpp" "test_node.cpp" + "test_asym_line.cpp" "test_line.cpp" "test_generic_branch.cpp" "test_link.cpp" diff --git a/tests/cpp_unit_tests/test_asym_line.cpp b/tests/cpp_unit_tests/test_asym_line.cpp new file mode 100644 index 000000000..929cc0b5a --- /dev/null +++ b/tests/cpp_unit_tests/test_asym_line.cpp @@ -0,0 +1,332 @@ +// SPDX-FileCopyrightText: Contributors to the Power Grid Model project +// +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include + +namespace power_grid_model { + +using namespace std::complex_literals; + +void execute_subcases(const AsymLineInput& input, const ComplexTensor& y_series, const double base_i, const double base_y, const double system_frequency, const double voltage_lvl) +{ + CAPTURE(input.id); + CAPTURE(y_series); + AsymLine asym_line{input, system_frequency, voltage_lvl, voltage_lvl}; + Branch& branch = asym_line; + ComplexTensor const y_shunt = 1 / base_y * (2 * pi * system_frequency * ComplexTensor{(2.0 * input.c1 + input.c0) / 3.0, (input.c0 - input.c1) / 3.0 } * 1.0i); + + DoubleComplex const y1_series = (y_series(0,0) + y_series(1,1) + y_series(2,2)) / 3.0 - (y_series(0,1) + y_series(1,2) + y_series(1,0) + y_series(1,2) + y_series(2,0) + y_series(2,1)) / 6.0; + DoubleComplex const y1_shunt = (y_shunt(0,0) + y_shunt(1,1) + y_shunt(2,2)) / 3.0 - (y_shunt(0,1) + y_shunt(1,2) + y_shunt(1,0) + y_shunt(1,2) + y_shunt(2,0) + y_shunt(2,1)) / 6.0; + + // symmetric + DoubleComplex const yff1 = y1_series + 0.5 * y1_shunt; + DoubleComplex const yft1 = -y1_series; + DoubleComplex const ys1 = 0.5 * y1_shunt + 1.0 / (1.0 / y1_series + 2.0 / y1_shunt); + + // asymmetric + ComplexTensor ytt = y_series + 0.5 * y_shunt; + ComplexTensor branch_shunt = 0.5 * inv(y_shunt) + inv(inv(y_series) + 2.0 * inv(y_shunt)); + + DoubleComplex const u1f = 1.0; + DoubleComplex const u1t = 0.9; + ComplexValue const uaf{1.0}; + ComplexValue const uat{0.9}; + DoubleComplex const i1f = (yff1 * u1f + yft1 * u1t) * base_i; + DoubleComplex const i1t = (yft1 * u1f + yff1 * u1t) * base_i; + DoubleComplex const s_f = conj(i1f) * u1f * 10e3 * sqrt3; + DoubleComplex const s_t = conj(i1t) * u1t * 10e3 * sqrt3; + double const loading = std::max(cabs(i1f), cabs(i1t)) / 200.0; + + // Short circuit results + DoubleComplex const if_sc{1.0, 1.0}; + DoubleComplex const it_sc{2.0, 2.0 * sqrt(3)}; + ComplexValue const if_sc_asym{1.0 + 1.0i}; + ComplexValue const it_sc_asym{2.0 + (2.0i * sqrt(3))}; + + CHECK(asym_line.math_model_type() == ComponentType::branch); + + SUBCASE("Voltge error") { CHECK_THROWS_AS(AsymLine(input, 50.0, 10.0e3, 50.0e3), ConflictVoltage); } + + SUBCASE("General") { + CHECK(branch.from_node() == 2); + CHECK(branch.to_node() == 3); + CHECK(branch.from_status() == true); + CHECK(branch.to_status() == true); + CHECK(branch.branch_status() == true); + CHECK(branch.status(BranchSide::from) == branch.from_status()); + CHECK(branch.status(BranchSide::to) == branch.to_status()); + CHECK(branch.base_i_from() == doctest::Approx(base_i)); + CHECK(branch.base_i_to() == doctest::Approx(base_i)); + CHECK(branch.phase_shift() == 0.0); + CHECK(!branch.is_param_mutable()); + } + + SUBCASE("Symmetric parameters") { + // double connected + BranchCalcParam param = branch.calc_param(); + CHECK(cabs(param.yff() - yff1) < numerical_tolerance); + CHECK(cabs(param.ytt() - yff1) < numerical_tolerance); + CHECK(cabs(param.ytf() - yft1) < numerical_tolerance); + CHECK(cabs(param.yft() - yft1) < numerical_tolerance); + // to connected + CHECK(branch.update(BranchUpdate{1, false, na_IntS}).topo); + param = branch.calc_param(); + CHECK(cabs(param.yff() - 0.0) < numerical_tolerance); + CHECK(cabs(param.ytt() - ys1) < numerical_tolerance); + CHECK(cabs(param.ytf() - 0.0) < numerical_tolerance); + CHECK(cabs(param.yft() - 0.0) < numerical_tolerance); + // not connected + CHECK(branch.set_status(na_IntS, false)); + param = branch.calc_param(); + CHECK(cabs(param.yff() - 0.0) < numerical_tolerance); + CHECK(cabs(param.ytt() - 0.0) < numerical_tolerance); + CHECK(cabs(param.ytf() - 0.0) < numerical_tolerance); + CHECK(cabs(param.yft() - 0.0) < numerical_tolerance); + // not changing + CHECK(!branch.set_status(false, false)); + // from connected + CHECK(branch.set_status(true, na_IntS)); + param = branch.calc_param(); + CHECK(cabs(param.yff() - ys1) < numerical_tolerance); + CHECK(cabs(param.ytt() - 0.0) < numerical_tolerance); + CHECK(cabs(param.ytf() - 0.0) < numerical_tolerance); + CHECK(cabs(param.yft() - 0.0) < numerical_tolerance); + } + + SUBCASE("Asymmetric parameters") { + // double connected + BranchCalcParam param = asym_line.calc_param(); + CHECK((cabs(param.yff() - ytt) < numerical_tolerance).all()); + CHECK((cabs(param.ytt() - ytt) < numerical_tolerance).all()); + CHECK((cabs(param.ytf() - (-y_series)) < numerical_tolerance).all()); + CHECK((cabs(param.yft() - (-y_series)) < numerical_tolerance).all()); + // no source + param = branch.calc_param(false); + CHECK((cabs(param.yff() - 0.0) < numerical_tolerance).all()); + CHECK((cabs(param.ytt() - 0.0) < numerical_tolerance).all()); + CHECK((cabs(param.ytf() - 0.0) < numerical_tolerance).all()); + CHECK((cabs(param.yft() - 0.0) < numerical_tolerance).all()); + // from connected + CHECK(branch.set_status(na_IntS, false)); + param = asym_line.calc_param(); + CHECK((cabs(param.yff() - branch_shunt) < numerical_tolerance).all()); // Fail + CHECK((cabs(param.ytt() - 0.0) < numerical_tolerance).all()); + CHECK((cabs(param.ytf() - 0.0) < numerical_tolerance).all()); + CHECK((cabs(param.yft() - 0.0) < numerical_tolerance).all()); + } + + SUBCASE("Symmetric results") { + BranchOutput output = branch.get_output(1.0, 0.9); + CHECK(output.id == 1); + CHECK(output.energized); + CHECK(output.loading == doctest::Approx(loading)); + CHECK(output.i_from == doctest::Approx(cabs(i1f))); + CHECK(output.i_to == doctest::Approx(cabs(i1t))); + CHECK(output.s_from == doctest::Approx(cabs(s_f))); + CHECK(output.s_to == doctest::Approx(cabs(s_t))); + CHECK(output.p_from == doctest::Approx(real(s_f))); + CHECK(output.p_to == doctest::Approx(real(s_t))); + CHECK(output.q_from == doctest::Approx(imag(s_f))); + CHECK(output.q_to == doctest::Approx(imag(s_t))); + } + + SUBCASE("Symmetric results with direct power and current output") { + BranchSolverOutput branch_solver_output{}; + branch_solver_output.i_f = 1.0 - 2.0i; + branch_solver_output.i_t = 2.0 - 1.0i; + branch_solver_output.s_f = 1.0 - 1.5i; + branch_solver_output.s_t = 1.5 - 1.5i; + BranchOutput output = branch.get_output(branch_solver_output); + CHECK(output.id == 1); + CHECK(output.energized); + CHECK(output.loading == doctest::Approx(cabs(2.0 - 1.0i) * base_i / input.i_n)); + CHECK(output.i_from == doctest::Approx(cabs(1.0 - 2.0i) * base_i)); + CHECK(output.i_to == doctest::Approx(cabs(2.0 - 1.0i) * base_i)); + CHECK(output.s_from == doctest::Approx(cabs(1.0 - 1.5i) * base_power)); + CHECK(output.s_to == doctest::Approx(cabs(1.5 - 1.5i) * base_power)); + CHECK(output.p_from == doctest::Approx(1.0 * base_power)); + CHECK(output.p_to == doctest::Approx(1.5 * base_power)); + CHECK(output.q_from == doctest::Approx(-1.5 * base_power)); + CHECK(output.q_to == doctest::Approx(-1.5 * base_power)); + } + + SUBCASE("No source results") { + BranchOutput output = branch.get_null_output(); + CHECK(output.id == 1); + CHECK(!output.energized); + CHECK(output.loading == 0.0); + CHECK(output.i_from(0) == 0.0); + CHECK(output.i_to(1) == 0.0); + CHECK(output.s_from(2) == 0.0); + CHECK(output.s_to(0) == 0.0); + CHECK(output.p_from(1) == 0.0); + CHECK(output.p_to(2) == 0.0); + CHECK(output.q_from(0) == 0.0); + CHECK(output.q_to(1) == 0.0); + } + + SUBCASE("No source short circuit results") { + BranchShortCircuitOutput output = branch.get_null_sc_output(); + CHECK(output.id == 1); + CHECK(!output.energized); + CHECK(output.i_from(0) == 0.0); + CHECK(output.i_to(1) == 0.0); + CHECK(output.i_from_angle(0) == 0.0); + CHECK(output.i_to_angle(1) == 0.0); + } + + SUBCASE("Asymmetric results") { + BranchOutput output = branch.get_output(uaf, uat); + CHECK(output.id == 1); + CHECK(output.energized); + CHECK(output.loading == doctest::Approx(loading)); + CHECK(output.i_from(0) == doctest::Approx(cabs(i1f))); + CHECK(output.i_to(1) == doctest::Approx(cabs(i1t))); + CHECK(output.s_from(2) == doctest::Approx(cabs(s_f) / 3.0)); + CHECK(output.s_to(0) == doctest::Approx(cabs(s_t) / 3.0)); + CHECK(output.p_from(1) == doctest::Approx(real(s_f) / 3.0)); + CHECK(output.p_to(2) == doctest::Approx(real(s_t) / 3.0)); + CHECK(output.q_from(0) == doctest::Approx(imag(s_f) / 3.0)); + CHECK(output.q_to(1) == doctest::Approx(imag(s_t) / 3.0)); + } + + SUBCASE("Asym short circuit results") { + BranchShortCircuitOutput asym_output = branch.get_sc_output(if_sc_asym, it_sc_asym); + CHECK(asym_output.id == 1); + CHECK(asym_output.energized); + CHECK(asym_output.i_from(1) == doctest::Approx(cabs(if_sc) * base_i)); + CHECK(asym_output.i_from(2) == doctest::Approx(cabs(if_sc) * base_i)); + CHECK(asym_output.i_to(0) == doctest::Approx(cabs(it_sc) * base_i)); + CHECK(asym_output.i_to(1) == doctest::Approx(cabs(it_sc) * base_i)); + CHECK(asym_output.i_from_angle(0) == doctest::Approx(pi / 4)); + CHECK(asym_output.i_from_angle(2) == doctest::Approx(pi / 4 + deg_120)); + CHECK(asym_output.i_to_angle(1) == doctest::Approx(pi / 3 - deg_120)); + CHECK(asym_output.i_to_angle(2) == doctest::Approx(pi / 3 + deg_120)); + CHECK(asym_output.id == 1); + } + + SUBCASE("Sym short circuit results") { + BranchShortCircuitOutput sym_output = branch.get_sc_output(if_sc, it_sc); + BranchShortCircuitOutput asym_output = branch.get_sc_output(if_sc_asym, it_sc_asym); + CHECK(sym_output.energized == asym_output.energized); + CHECK(sym_output.i_from(1) == doctest::Approx(asym_output.i_from(1))); + CHECK(sym_output.i_from(2) == doctest::Approx(asym_output.i_from(2))); + CHECK(sym_output.i_to(0) == doctest::Approx(asym_output.i_to(0))); + CHECK(sym_output.i_to(1) == doctest::Approx(asym_output.i_to(1))); + CHECK(sym_output.i_from_angle(0) == doctest::Approx(asym_output.i_from_angle(0))); + CHECK(sym_output.i_from_angle(2) == doctest::Approx(asym_output.i_from_angle(2))); + CHECK(sym_output.i_to_angle(1) == doctest::Approx(asym_output.i_to_angle(1))); + CHECK(sym_output.i_to_angle(2) == doctest::Approx(asym_output.i_to_angle(2))); + } + + SUBCASE("Update inverse") { + BranchUpdate branch_update{1, na_IntS, na_IntS}; + auto expected = branch_update; + + SUBCASE("Identical") { + // default values + } + + SUBCASE("From status") { + SUBCASE("same") { branch_update.from_status = static_cast(asym_line.from_status()); } + SUBCASE("different") { branch_update.from_status = IntS{0}; } + expected.from_status = static_cast(asym_line.from_status()); + } + + SUBCASE("To status") { + SUBCASE("same") { branch_update.to_status = static_cast(asym_line.to_status()); } + SUBCASE("different") { branch_update.to_status = IntS{0}; } + expected.to_status = static_cast(asym_line.to_status()); + } + + SUBCASE("multiple") { + branch_update.from_status = IntS{0}; + branch_update.to_status = IntS{0}; + expected.from_status = static_cast(asym_line.from_status()); + expected.to_status = static_cast(asym_line.to_status()); + } + + auto const inv = asym_line.inverse(branch_update); + + CHECK(inv.id == expected.id); + CHECK(inv.from_status == expected.from_status); + CHECK(inv.to_status == expected.to_status); + } +} + +TEST_CASE("Test asym line") { + + double system_frequency = 50.0; + double voltage_lvl = 10.0e3; + double const base_i = base_power_1p / (voltage_lvl / sqrt3); + double const base_y = base_i * base_i / base_power_1p; + + SUBCASE("R and X matrix c0, c1 including neutral") { + AsymLineInput input = {.id = 1, + .from_node = 2, + .to_node = 3, + .from_status = 1, + .to_status = 1, + .r_aa = 0.4369, + .r_ba = 0.0496, + .r_bb = 0.4369, + .r_ca = 0.0485, + .r_cb = 0.0496, + .r_cc = 0.4369, + .r_na = 0.0496, + .r_nb = 0.0485, + .r_nc = 0.0496, + .r_nn = 0.4369, + .x_aa = 0.8538, + .x_ba = 0.7886, + .x_bb = 0.8538, + .x_ca = 0.7663, + .x_cb = 0.7886, + .x_cc = 0.8538, + .x_na = 0.7886, + .x_nb = 0.7663, + .x_nc = 0.7886, + .x_nn = 0.8538, + .c0 = 0.18, + .c1 = 0.308, + .i_n = 216.0}; + + ComplexTensor4 r_matrix = ComplexTensor4(input.r_aa, input.r_bb, input.r_cc, input.r_nn, input.r_ba, input.r_ca, input.r_na, input.r_cb, input.r_nb, input.r_nc); + ComplexTensor4 x_matrix = ComplexTensor4(input.x_aa, input.x_bb, input.x_cc, input.x_nn, input.x_ba, input.x_ca, input.x_na, input.x_cb, input.x_nb, input.x_nc); + ComplexTensor4 z = r_matrix + 1.0i * x_matrix; + ComplexTensor const y_series = 1 / base_y * inv(kron_reduction(z)); + execute_subcases(input, y_series, base_i, base_y, system_frequency, voltage_lvl); + } + + SUBCASE("R and X matrix, c0, c1 excluding neutral") { + AsymLineInput input = {.id = 2, + .from_node = 2, + .to_node = 3, + .from_status = 1, + .to_status = 1, + .r_aa = 0.4369, + .r_ba = 0.0496, + .r_bb = 0.4369, + .r_ca = 0.0485, + .r_cb = 0.0496, + .r_cc = 0.4369, + .x_aa = 0.8538, + .x_ba = 0.7886, + .x_bb = 0.8538, + .x_ca = 0.7663, + .x_cb = 0.7886, + .x_cc = 0.8538, + .c0 = 0.18, + .c1 = 0.308, + .i_n = 216.0}; + ComplexTensor r_matrix = ComplexTensor(input.r_aa, input.r_bb, input.r_cc, input.r_ba, input.r_ca, input.r_cb); + ComplexTensor x_matrix = ComplexTensor(input.x_aa, input.x_bb, input.x_cc, input.x_ba, input.x_ca, input.x_cb); + ComplexTensor const y_series = 1 / base_y * inv(r_matrix + 1.0i * x_matrix); + execute_subcases(input, y_series, base_i, base_y, system_frequency, voltage_lvl); + } +} + +} // namespace power_grid_model diff --git a/tests/unit/validation/test_input_validation.py b/tests/unit/validation/test_input_validation.py index aef0061b5..eef712d0b 100644 --- a/tests/unit/validation/test_input_validation.py +++ b/tests/unit/validation/test_input_validation.py @@ -24,6 +24,7 @@ InvalidAssociatedEnumValueError, InvalidEnumValueError, InvalidIdError, + MissingValueError, MultiComponentNotUniqueError, NotBetweenError, NotBetweenOrAtError, @@ -57,6 +58,46 @@ def original_data() -> dict[ComponentType, np.ndarray]: line["x0"] = [0, 0, 50] line["i_n"] = [-3, 0, 50] + asym_line = initialize_array(DatasetType.input, ComponentType.asym_line, 2) + asym_line["id"] = [55, 56] + asym_line["from_node"] = [0, 1] + asym_line["to_node"] = [1, 2] + asym_line["from_status"] = [1, 1] + asym_line["to_status"] = [1, 1] + asym_line["r_aa"] = [-1, 2] + asym_line["r_ba"] = [-1, 2] + asym_line["r_bb"] = [-1, 2] + asym_line["r_ca"] = [-1, 2] + asym_line["r_cb"] = [-1, 2] + asym_line["r_cc"] = [-1, 2] + asym_line["r_na"] = [-1, 2] + asym_line["r_nb"] = [-1, 2] + asym_line["r_nc"] = [-1, 2] + asym_line["r_nn"] = [-1, 2] + asym_line["x_aa"] = [-1, 2] + asym_line["x_ba"] = [-1, 2] + asym_line["x_bb"] = [-1, 2] + asym_line["x_ca"] = [-1, 2] + asym_line["x_cb"] = [-1, 2] + asym_line["x_cc"] = [-1, 2] + asym_line["x_na"] = [-1, 2] + asym_line["x_nb"] = [-1, 2] + asym_line["x_nc"] = [-1, 2] + asym_line["x_nn"] = [-1, 2] + asym_line["c_aa"] = [-1, np.nan] + asym_line["c_ba"] = [-1, np.nan] + asym_line["c_bb"] = [-1, np.nan] + asym_line["c_ca"] = [-1, np.nan] + asym_line["c_cb"] = [-1, np.nan] + asym_line["c_cc"] = [-1, np.nan] + asym_line["c_na"] = [-1, np.nan] + asym_line["c_nb"] = [-1, np.nan] + asym_line["c_nc"] = [-1, np.nan] + asym_line["c_nn"] = [-1, np.nan] + asym_line["c0"] = [-1, np.nan] + asym_line["c1"] = [-1, np.nan] + asym_line["i_n"] = [50, 50] + generic_branch = initialize_array(DatasetType.input, ComponentType.generic_branch, 1) generic_branch["id"] = [6] generic_branch["from_node"] = [1] @@ -270,10 +311,10 @@ def original_data() -> dict[ComponentType, np.ndarray]: fault["fault_object"] = [200, 3] + list(range(10, 28, 2)) + 9 * [0] fault["r_f"] = [-1.0, 0.0, 1.0] + 17 * [_nan_type("fault", "r_f")] fault["x_f"] = [-1.0, 0.0, 1.0] + 17 * [_nan_type("fault", "x_f")] - data = { ComponentType.node: node, ComponentType.line: line, + ComponentType.asym_line: asym_line, ComponentType.generic_branch: generic_branch, ComponentType.link: link, ComponentType.transformer: transformer, @@ -692,3 +733,51 @@ def test_generic_branch_input_data(input_data): validation_errors = validate_input_data(input_data, symmetric=True) assert NotGreaterThanError("generic_branch", "k", [6], 0) in validation_errors assert NotGreaterOrEqualError("generic_branch", "sn", [6], 0) in validation_errors + + +def test_asym_line_input_data(input_data): + validation_errors = validate_input_data(input_data, symmetric=True) + assert NotGreaterThanError(ComponentType.asym_line, "r_aa", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "r_ba", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "r_bb", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "r_ca", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "r_cb", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "r_cc", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "r_na", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "r_nb", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "r_nc", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "r_nn", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "x_aa", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "x_ba", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "x_bb", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "x_ca", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "x_cb", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "x_cc", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "x_na", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "x_nb", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "x_nc", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "x_nn", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c_aa", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c_ba", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c_bb", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c_ca", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c_cb", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c_cc", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c_na", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c_nb", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c_nc", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c_nn", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c0", [55], 0) in validation_errors + assert NotGreaterThanError(ComponentType.asym_line, "c1", [55], 0) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c_aa", [56]) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c_ba", [56]) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c_bb", [56]) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c_ca", [56]) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c_cb", [56]) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c_cc", [56]) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c_na", [56]) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c_nb", [56]) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c_nc", [56]) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c_nn", [56]) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c0", [56]) in validation_errors + assert MissingValueError(ComponentType.asym_line, "c1", [56]) in validation_errors