diff --git a/code_generation/data/attribute_classes/input.json b/code_generation/data/attribute_classes/input.json index 5edbd3236..c47cebc5c 100644 --- a/code_generation/data/attribute_classes/input.json +++ b/code_generation/data/attribute_classes/input.json @@ -127,6 +127,33 @@ } ] }, + { + "name": "GenericBranchInput", + "base": "BranchInput", + "attributes": [ + { + "data_type": "double", + "names": ["r1","x1","g1","b1"], + "description": "positive sequence parameters" + }, + { + "data_type": "double", + "names": "k", + "description": "transformer ratio, default = 1.0" + }, + { + "data_type": "double", + "names": "theta", + "description": "angle shift" + }, + { + "data_type": "double", + "names": "sn", + "description": "rated power for calculation of loading (obsolete)" + } + ] + }, + { "name": "LinkInput", "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 5574337d5..e4cb9073d 100644 --- a/code_generation/data/dataset_class_maps/dataset_definitions.json +++ b/code_generation/data/dataset_class_maps/dataset_definitions.json @@ -16,6 +16,10 @@ "names": ["link"], "class_name": "LinkInput" }, + { + "names": ["generic_branch"], + "class_name": "GenericBranchInput" + }, { "names": ["transformer"], "class_name": "TransformerInput" @@ -63,7 +67,7 @@ "class_name": "NodeOutput" }, { - "names": ["line", "link", "transformer"], + "names": ["line", "link", "transformer", "generic_branch"], "class_name": "BranchOutput" }, { 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 3c53c3e32..21087006a 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 @@ -10,6 +10,7 @@ // component include #include "component/appliance.hpp" #include "component/fault.hpp" +#include "component/genericbranch.hpp" #include "component/line.hpp" #include "component/link.hpp" #include "component/load_gen.hpp" @@ -25,8 +26,9 @@ namespace power_grid_model { -using AllComponents = ComponentList; +using AllComponents = + ComponentList; } // namespace power_grid_model 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 4077b4e02..e5db8e461 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,29 @@ struct LineInput { 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 + 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 r1{nan}; // positive sequence parameters + double x1{nan}; // positive sequence parameters + double g1{nan}; // positive sequence parameters + double b1{nan}; // positive sequence parameters + double k{nan}; // transformer ratio, default = 1.0 + double theta{nan}; // angle shift + double sn{nan}; // rated power for calculation of loading (obsolete) + + // 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 LinkInput { 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 dfc3f2f31..7cca2754f 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,26 @@ 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<&GenericBranchInput::id>(offsetof(GenericBranchInput, id), "id"), + meta_data_gen::get_meta_attribute<&GenericBranchInput::from_node>(offsetof(GenericBranchInput, from_node), "from_node"), + meta_data_gen::get_meta_attribute<&GenericBranchInput::to_node>(offsetof(GenericBranchInput, to_node), "to_node"), + meta_data_gen::get_meta_attribute<&GenericBranchInput::from_status>(offsetof(GenericBranchInput, from_status), "from_status"), + meta_data_gen::get_meta_attribute<&GenericBranchInput::to_status>(offsetof(GenericBranchInput, to_status), "to_status"), + meta_data_gen::get_meta_attribute<&GenericBranchInput::r1>(offsetof(GenericBranchInput, r1), "r1"), + meta_data_gen::get_meta_attribute<&GenericBranchInput::x1>(offsetof(GenericBranchInput, x1), "x1"), + meta_data_gen::get_meta_attribute<&GenericBranchInput::g1>(offsetof(GenericBranchInput, g1), "g1"), + meta_data_gen::get_meta_attribute<&GenericBranchInput::b1>(offsetof(GenericBranchInput, b1), "b1"), + meta_data_gen::get_meta_attribute<&GenericBranchInput::k>(offsetof(GenericBranchInput, k), "k"), + meta_data_gen::get_meta_attribute<&GenericBranchInput::theta>(offsetof(GenericBranchInput, theta), "theta"), + meta_data_gen::get_meta_attribute<&GenericBranchInput::sn>(offsetof(GenericBranchInput, sn), "sn"), + }; +}; + 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 e8a7c8354..ee2288a36 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 GenericBranchInput +static_assert(std::is_standard_layout_v); +// static asserts for conversion of GenericBranchInput to BaseInput +static_assert(std::alignment_of_v >= std::alignment_of_v); +static_assert(std::same_as); +static_assert(offsetof(GenericBranchInput, id) == offsetof(BaseInput, id)); +// static asserts for conversion of GenericBranchInput 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(GenericBranchInput, id) == offsetof(BranchInput, id)); +static_assert(offsetof(GenericBranchInput, from_node) == offsetof(BranchInput, from_node)); +static_assert(offsetof(GenericBranchInput, to_node) == offsetof(BranchInput, to_node)); +static_assert(offsetof(GenericBranchInput, from_status) == offsetof(BranchInput, from_status)); +static_assert(offsetof(GenericBranchInput, to_status) == offsetof(BranchInput, to_status)); + // static asserts for LinkInput static_assert(std::is_standard_layout_v); // static asserts for conversion of LinkInput to BaseInput diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp index 5bcb632dc..ab4c8f731 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp @@ -166,6 +166,11 @@ class DuplicativelyRegulatedObject : public PowerGridError { } }; +class NotImplementedError : public PowerGridError { + public: + NotImplementedError() { append_msg("Function not yet implemented"); } +}; + class AutomaticTapCalculationError : public PowerGridError { public: AutomaticTapCalculationError(ID id) { diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/component/genericbranch.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/component/genericbranch.hpp new file mode 100644 index 000000000..875db2837 --- /dev/null +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/component/genericbranch.hpp @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Contributors to the Power Grid Model project +// +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#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/exception.hpp" +#include "../common/three_phase_tensor.hpp" + +/* + generic Branch: either a line N = 1 or a transformoer N = t*e^(j*theta1) + paramaters should be given as r1, x1, .... + + -----| |-----------y1_series------- + | | | | + | | y1_shunt y1_shunt + | | | | + | | | | + -----| |-------------------------- + N = k * e^(j*theta1) + +*/ + +namespace power_grid_model { + +class GenericBranch final : public Branch { + public: + using InputType = GenericBranchInput; + using UpdateType = BranchUpdate; + static constexpr char const* name = "generic_branch"; + + explicit GenericBranch(GenericBranchInput const& genericbranch_input, double u1_rated, double u2_rated) + : Branch{genericbranch_input}, + sn_{genericbranch_input.sn}, + r1_{genericbranch_input.r1}, + x1_{genericbranch_input.x1}, + g1_{genericbranch_input.g1}, + b1_{genericbranch_input.b1}, + k_{std::isnan(genericbranch_input.k) ? 1.0 : genericbranch_input.k}, + theta_{std::isnan(genericbranch_input.theta) ? 0.0 : std::fmod(genericbranch_input.theta, 2 * pi)}, + base_i_from_{base_power_3p / u1_rated / sqrt3}, + base_i_to_{base_power_3p / u2_rated / sqrt3}, + base_y_{base_i_to_ / (u2_rated / sqrt3)} { + + y1_series_ = 1.0 / (r1_ + 1.0i * x1_) / base_y_; + y1_shunt_ = (g1_ + 1.0i * b1_) / base_y_; + } + + // override getter + double base_i_from() const override { return base_i_from_; } + double base_i_to() const override { return base_i_to_; } + double loading(double max_s, double /*max_i*/) const override { return std::isnan(sn_) ? 0.0 : (max_s / sn_); }; + double phase_shift() const override { return theta_; } + bool is_param_mutable() const override { return false; } + + private: + double sn_; + double r1_; + double x1_; + double g1_; + double b1_; + double k_; + double theta_; + double base_i_from_; + double base_i_to_; + double base_y_; + DoubleComplex y1_series_; + DoubleComplex y1_shunt_; + + BranchCalcParam sym_calc_param() const override { + return calc_param_y_sym(y1_series_, y1_shunt_, k_ * std::exp(1.0i * theta_)); + } + + [[noreturn]] BranchCalcParam asym_calc_param() const override { throw NotImplementedError(); } +}; + +} // namespace power_grid_model 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 4ebd40e85..3be340891 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 @@ -55,6 +55,21 @@ PGM_API extern PGM_MetaAttribute const* const PGM_def_input_link_from_node; PGM_API extern PGM_MetaAttribute const* const PGM_def_input_link_to_node; PGM_API extern PGM_MetaAttribute const* const PGM_def_input_link_from_status; PGM_API extern PGM_MetaAttribute const* const PGM_def_input_link_to_status; +// component generic_branch +PGM_API extern PGM_MetaComponent const* const PGM_def_input_generic_branch; +// attributes of input generic_branch +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_id; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_from_node; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_to_node; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_from_status; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_to_status; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_r1; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_x1; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_g1; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_b1; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_k; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_theta; +PGM_API extern PGM_MetaAttribute const* const PGM_def_input_generic_branch_sn; // component transformer PGM_API extern PGM_MetaComponent const* const PGM_def_input_transformer; // attributes of input transformer @@ -311,6 +326,20 @@ PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_transformer_p_t PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_transformer_q_to; PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_transformer_i_to; PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_transformer_s_to; +// component generic_branch +PGM_API extern PGM_MetaComponent const* const PGM_def_sym_output_generic_branch; +// attributes of sym_output generic_branch +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_id; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_energized; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_loading; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_p_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_q_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_i_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_s_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_p_to; +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 transformer_tap_regulator PGM_API extern PGM_MetaComponent const* const PGM_def_sym_output_transformer_tap_regulator; // attributes of sym_output transformer_tap_regulator @@ -483,6 +512,20 @@ PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_transformer_p_ PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_transformer_q_to; PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_transformer_i_to; PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_transformer_s_to; +// component generic_branch +PGM_API extern PGM_MetaComponent const* const PGM_def_asym_output_generic_branch; +// attributes of asym_output generic_branch +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_id; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_energized; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_loading; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_p_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_q_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_i_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_s_from; +PGM_API extern PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_p_to; +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 transformer_tap_regulator PGM_API extern PGM_MetaComponent const* const PGM_def_asym_output_transformer_tap_regulator; // attributes of asym_output transformer_tap_regulator 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 52951134e..9639a9755 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 @@ -44,6 +44,21 @@ PGM_MetaAttribute const* const PGM_def_input_link_from_node = PGM_meta_get_attri PGM_MetaAttribute const* const PGM_def_input_link_to_node = PGM_meta_get_attribute_by_name(nullptr, "input", "link", "to_node"); PGM_MetaAttribute const* const PGM_def_input_link_from_status = PGM_meta_get_attribute_by_name(nullptr, "input", "link", "from_status"); PGM_MetaAttribute const* const PGM_def_input_link_to_status = PGM_meta_get_attribute_by_name(nullptr, "input", "link", "to_status"); +// component generic_branch +PGM_MetaComponent const* const PGM_def_input_generic_branch = PGM_meta_get_component_by_name(nullptr, "input", "generic_branch"); +// attributes of input generic_branch +PGM_MetaAttribute const* const PGM_def_input_generic_branch_id = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "id"); +PGM_MetaAttribute const* const PGM_def_input_generic_branch_from_node = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "from_node"); +PGM_MetaAttribute const* const PGM_def_input_generic_branch_to_node = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "to_node"); +PGM_MetaAttribute const* const PGM_def_input_generic_branch_from_status = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "from_status"); +PGM_MetaAttribute const* const PGM_def_input_generic_branch_to_status = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "to_status"); +PGM_MetaAttribute const* const PGM_def_input_generic_branch_r1 = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "r1"); +PGM_MetaAttribute const* const PGM_def_input_generic_branch_x1 = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "x1"); +PGM_MetaAttribute const* const PGM_def_input_generic_branch_g1 = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "g1"); +PGM_MetaAttribute const* const PGM_def_input_generic_branch_b1 = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "b1"); +PGM_MetaAttribute const* const PGM_def_input_generic_branch_k = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "k"); +PGM_MetaAttribute const* const PGM_def_input_generic_branch_theta = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "theta"); +PGM_MetaAttribute const* const PGM_def_input_generic_branch_sn = PGM_meta_get_attribute_by_name(nullptr, "input", "generic_branch", "sn"); // component transformer PGM_MetaComponent const* const PGM_def_input_transformer = PGM_meta_get_component_by_name(nullptr, "input", "transformer"); // attributes of input transformer @@ -300,6 +315,20 @@ PGM_MetaAttribute const* const PGM_def_sym_output_transformer_p_to = PGM_meta_ge PGM_MetaAttribute const* const PGM_def_sym_output_transformer_q_to = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "transformer", "q_to"); PGM_MetaAttribute const* const PGM_def_sym_output_transformer_i_to = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "transformer", "i_to"); PGM_MetaAttribute const* const PGM_def_sym_output_transformer_s_to = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "transformer", "s_to"); +// component generic_branch +PGM_MetaComponent const* const PGM_def_sym_output_generic_branch = PGM_meta_get_component_by_name(nullptr, "sym_output", "generic_branch"); +// attributes of sym_output generic_branch +PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_id = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "generic_branch", "id"); +PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_energized = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "generic_branch", "energized"); +PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_loading = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "generic_branch", "loading"); +PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_p_from = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "generic_branch", "p_from"); +PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_q_from = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "generic_branch", "q_from"); +PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_i_from = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "generic_branch", "i_from"); +PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_s_from = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "generic_branch", "s_from"); +PGM_MetaAttribute const* const PGM_def_sym_output_generic_branch_p_to = PGM_meta_get_attribute_by_name(nullptr, "sym_output", "generic_branch", "p_to"); +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 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 @@ -472,6 +501,20 @@ PGM_MetaAttribute const* const PGM_def_asym_output_transformer_p_to = PGM_meta_g PGM_MetaAttribute const* const PGM_def_asym_output_transformer_q_to = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "transformer", "q_to"); PGM_MetaAttribute const* const PGM_def_asym_output_transformer_i_to = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "transformer", "i_to"); PGM_MetaAttribute const* const PGM_def_asym_output_transformer_s_to = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "transformer", "s_to"); +// component generic_branch +PGM_MetaComponent const* const PGM_def_asym_output_generic_branch = PGM_meta_get_component_by_name(nullptr, "asym_output", "generic_branch"); +// attributes of asym_output generic_branch +PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_id = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "generic_branch", "id"); +PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_energized = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "generic_branch", "energized"); +PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_loading = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "generic_branch", "loading"); +PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_p_from = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "generic_branch", "p_from"); +PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_q_from = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "generic_branch", "q_from"); +PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_i_from = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "generic_branch", "i_from"); +PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_s_from = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "generic_branch", "s_from"); +PGM_MetaAttribute const* const PGM_def_asym_output_generic_branch_p_to = PGM_meta_get_attribute_by_name(nullptr, "asym_output", "generic_branch", "p_to"); +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 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 diff --git a/src/power_grid_model/core/dataset_definitions.py b/src/power_grid_model/core/dataset_definitions.py index a8d3d4762..d8f8031b8 100644 --- a/src/power_grid_model/core/dataset_definitions.py +++ b/src/power_grid_model/core/dataset_definitions.py @@ -57,6 +57,7 @@ class ComponentType(str, Enum, metaclass=_MetaEnum): node = "node" line = "line" link = "link" + generic_branch = "generic_branch" transformer = "transformer" transformer_tap_regulator = "transformer_tap_regulator" three_winding_transformer = "three_winding_transformer" diff --git a/src/power_grid_model/validation/validation.py b/src/power_grid_model/validation/validation.py index e904f63fd..812326474 100644 --- a/src/power_grid_model/validation/validation.py +++ b/src/power_grid_model/validation/validation.py @@ -467,6 +467,7 @@ def validate_values(data: SingleDataset, calculation_type: Optional[CalculationT "node": validate_node, "line": validate_line, "link": lambda d: validate_branch(d, ComponentType.link), + "generic_branch": validate_genericbranch, "transformer": validate_transformer, "three_winding_transformer": validate_three_winding_transformer, "source": validate_source, @@ -532,6 +533,12 @@ def validate_line(data: SingleDataset) -> list[ValidationError]: return errors +def validate_genericbranch(data: SingleDataset) -> list[ValidationError]: + errors = validate_branch(data, ComponentType.generic_branch) + + return errors + + def validate_transformer(data: SingleDataset) -> list[ValidationError]: errors = validate_branch(data, ComponentType.transformer) errors += all_greater_than_zero(data, ComponentType.transformer, "u1") @@ -844,6 +851,7 @@ def validate_generic_power_sensor(data: SingleDataset, component: ComponentType) ref_components=[ ComponentType.node, ComponentType.line, + ComponentType.generic_branch, ComponentType.transformer, ComponentType.three_winding_transformer, ComponentType.source, @@ -858,14 +866,14 @@ def validate_generic_power_sensor(data: SingleDataset, component: ComponentType) data, component, field="measured_object", - ref_components=[ComponentType.line, ComponentType.transformer], + ref_components=[ComponentType.line, ComponentType.generic_branch, ComponentType.transformer], measured_terminal_type=MeasuredTerminalType.branch_from, ) errors += all_valid_ids( data, component, field="measured_object", - ref_components=[ComponentType.line, ComponentType.transformer], + ref_components=[ComponentType.line, ComponentType.generic_branch, ComponentType.transformer], measured_terminal_type=MeasuredTerminalType.branch_to, ) errors += all_valid_ids( diff --git a/tests/cpp_unit_tests/CMakeLists.txt b/tests/cpp_unit_tests/CMakeLists.txt index 73f908b53..8745a1fae 100644 --- a/tests/cpp_unit_tests/CMakeLists.txt +++ b/tests/cpp_unit_tests/CMakeLists.txt @@ -14,6 +14,7 @@ set(PROJECT_SOURCES "test_three_phase_tensor.cpp" "test_node.cpp" "test_line.cpp" + "test_genericbranch.cpp" "test_link.cpp" "test_load_gen.cpp" "test_source.cpp" diff --git a/tests/cpp_unit_tests/test_all_components.cpp b/tests/cpp_unit_tests/test_all_components.cpp index 08c05bc85..d8768c58e 100644 --- a/tests/cpp_unit_tests/test_all_components.cpp +++ b/tests/cpp_unit_tests/test_all_components.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ namespace power_grid_model { static_assert(component_c); static_assert(component_c); static_assert(component_c); +static_assert(component_c); static_assert(component_c); static_assert(component_c); static_assert(component_c); @@ -54,6 +56,10 @@ static_assert(is_copyable_to); static_assert(!is_copyable_to); static_assert(!is_copyable_to); +static_assert(is_copyable_to); +static_assert(!is_copyable_to); +static_assert(!is_copyable_to); + static_assert(is_copyable_to); static_assert(!is_copyable_to); static_assert(!is_copyable_to); diff --git a/tests/cpp_unit_tests/test_genericbranch.cpp b/tests/cpp_unit_tests/test_genericbranch.cpp new file mode 100644 index 000000000..67a5cfd8b --- /dev/null +++ b/tests/cpp_unit_tests/test_genericbranch.cpp @@ -0,0 +1,219 @@ +// SPDX-FileCopyrightText: Contributors to the Power Grid Model project +// +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + +#include + +namespace power_grid_model { + +using namespace std::complex_literals; + +TEST_CASE("Test generic_branch") { + constexpr double u1 = 150e3; + constexpr double u2 = 10.0e3; + constexpr double base_i_from = base_power_3p / u1 / sqrt3; + constexpr double base_i_to = base_power_3p / u2 / sqrt3; + + GenericBranchInput const input{.id = 1, + .from_node = 2, + .to_node = 3, + .from_status = 1, + .to_status = 1, + .r1 = 0.016, + .x1 = 0.16, + .g1 = 0.0, + .b1 = 0.0, + .k = 1.0, + .theta = 0.0, + .sn = 30e6}; + + GenericBranch branch{input, u1, u2}; + double const base_y = base_i_to * base_i_to / base_power_1p; + + DoubleComplex const y1_series = 1.0 / (input.r1 + 1.0i * input.x1) / base_y; + DoubleComplex const y1_shunt = (input.g1 + 1.0i * input.b1) / base_y; + + // symmetric + DoubleComplex const yff1 = y1_series + 0.5 * y1_shunt; + DoubleComplex const yft1 = -y1_series; + + 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_from)); + CHECK(branch.base_i_to() == doctest::Approx(base_i_to)); + CHECK(branch.phase_shift() == 0.0); + CHECK(!branch.is_param_mutable()); + } + + SUBCASE("Symmetric parameters") { + 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); + } + + SUBCASE("Test i base") { + CHECK(base_i_from == doctest::Approx(base_i_from)); + CHECK(base_i_to == doctest::Approx(base_i_to)); + } + + SUBCASE("Test genericbranch phase shift") { CHECK(branch.phase_shift() == doctest::Approx(0.0)); } + + SUBCASE("Test genericbranch loading") { CHECK(branch.loading(60.0e6, 0.0) == doctest::Approx(2.0)); } +} + +TEST_CASE("Test compare_generic_branch") { + constexpr double ratio = 1.03; + constexpr double u1 = 1e4; + constexpr double u2 = 4e2; + + constexpr double u1_rated = u1; + constexpr double u2_rated = ratio * 4e2; // ensures that the transformer ratio matches the generic branch ratio + + constexpr double base_i_to = base_power_3p / u2_rated / sqrt3; + constexpr double base_y = base_i_to * base_i_to / base_power_1p; + + GenericBranchInput const genb_input{.id = 1, + .from_node = 2, + .to_node = 3, + .from_status = 1, + .to_status = 1, + .r1 = 0.016, + .x1 = 0.159198, + .g1 = 6.25e-08, + .b1 = -6.21867e-07, + .k = 1.03, + .theta = 0.0, + .sn = 1e5}; + + GenericBranch gen_branch{genb_input, u1_rated, u2_rated}; + + double const theta{gen_branch.phase_shift()}; + + DoubleComplex const genb_ratio = genb_input.k * std::exp(1.0i * theta); + DoubleComplex const genb_abs = std::abs(genb_ratio); + DoubleComplex const z1_series = genb_input.r1 + 1.0i * genb_input.x1; + DoubleComplex const y1_series = 1.0 / z1_series / base_y; + DoubleComplex const y1_shunt = (genb_input.g1 + 1.0i * genb_input.b1) / base_y; + + SUBCASE("Symmetric genbranch_parameters") { + BranchCalcParam param1 = gen_branch.calc_param(); + // symmetric + DoubleComplex const ytt1 = y1_series + 0.5 * y1_shunt; + DoubleComplex const yff1 = (1.0 / genb_abs / genb_abs) * ytt1; + DoubleComplex const yft1 = (-1.0 / conj(genb_ratio)) * y1_series; + DoubleComplex const ytf1 = (-1.0 / genb_ratio) * y1_series; + + CHECK(cabs(param1.yff() - yff1) < numerical_tolerance); + CHECK(cabs(param1.ytt() - ytt1) < numerical_tolerance); + CHECK(cabs(param1.ytf() - ytf1) < numerical_tolerance); + CHECK(cabs(param1.yft() - yft1) < numerical_tolerance); + } + + TransformerInput trans_input{ + .id = 1, + .from_node = 2, + .to_node = 3, + .from_status = 1, + .to_status = 1, + .u1 = 1e4, + .u2 = 4e2, + .sn = 1e5, + .uk = 0.1, + .pk = 1e3, + .i0 = 1.0e-6, + .p0 = 0.01, + .winding_from = WindingType::wye_n, + .winding_to = WindingType::wye_n, + .clock = 12, + .tap_side = BranchSide::from, + .tap_pos = 0, // tap_pos influences uk and p0, which results in modified z_series and y_shunt values! + .tap_min = -11, + .tap_max = 9, + .tap_nom = 0, + .tap_size = 100.0, + .uk_min = nan, + .uk_max = nan, + .pk_min = nan, + .pk_max = nan, + .r_grounding_from = nan, + .x_grounding_from = nan, + .r_grounding_to = nan, + .x_grounding_to = nan}; + + double const sn{trans_input.sn}; + double const pk{trans_input.pk}; + double const p0{trans_input.p0}; + double const uk{trans_input.uk}; + double const nominal_ratio{u1_rated / u2_rated}; + double const k{(trans_input.u1 / trans_input.u2) / nominal_ratio}; + double const i0{trans_input.i0}; + + Transformer transformer(trans_input, u1_rated, u2_rated); + + DoubleComplex const trafo_ratio = k * std::exp(1.0i * (transformer.phase_shift())); + double ratio_abs = std::abs(trafo_ratio); + + // y_series: + double const z_series_abs = uk * u2 * u2 / sn; + double const r_series = pk * u2 * u2 / sn / sn; + double const z_series_imag_squared = z_series_abs * z_series_abs - r_series * r_series; + double const z_series_imag = (z_series_imag_squared > 0.0 ? std::sqrt(z_series_imag_squared) : 0.0); + DoubleComplex const z_series = r_series + 1.0i * z_series_imag; + DoubleComplex const y_series = 1.0 / z_series / base_y; + + // y_shunt: + DoubleComplex y_shunt; + double const y_shunt_abs = i0 * sn / u2 / u2; + y_shunt.real(p0 / u2 / u2); + double const y_shunt_imag_squared = y_shunt_abs * y_shunt_abs - y_shunt.real() * y_shunt.real(); + y_shunt.imag(y_shunt_imag_squared > 0.0 ? -std::sqrt(y_shunt_imag_squared) : 0.0); + y_shunt = y_shunt / base_y; + + SUBCASE("Tap position") { CHECK((trans_input.tap_pos - trans_input.tap_nom) == 0); } + + SUBCASE("Symmetric trafo_parameters") { + BranchCalcParam param = transformer.calc_param(); + + DoubleComplex const ytt = y_series + 0.5 * y_shunt; + DoubleComplex const yff = (1.0 / ratio_abs / ratio_abs) * ytt; + DoubleComplex const yft = (-1.0 / conj(trafo_ratio)) * y_series; + DoubleComplex const ytf = (-1.0 / trafo_ratio) * y_series; + + CHECK(cabs(param.yff() - yff) < numerical_tolerance); + CHECK(cabs(param.ytt() - ytt) < numerical_tolerance); + CHECK(cabs(param.ytf() - ytf) < numerical_tolerance); + CHECK(cabs(param.yft() - yft) < numerical_tolerance); + } + + SUBCASE("Symmetric compare Y-Parameters") { + BranchCalcParam param1 = gen_branch.calc_param(); + BranchCalcParam param = transformer.calc_param(); + + CHECK(cabs((trafo_ratio - genb_ratio)) < numerical_tolerance); + + CHECK((y_shunt.real() - y1_shunt.real()) < 1e-6); + CHECK((y_shunt.imag() - y1_shunt.imag()) < 1e-6); + CHECK((y_series.real() - y1_series.real()) < 1e-6); + CHECK((y_series.imag() - y1_series.imag()) < 1e-6); + + CHECK(cabs(param1.yff() - param.yff()) < 1e-6); + CHECK(cabs(param1.ytt() - param.ytt()) < 1e-6); + CHECK(cabs(param1.ytf() - param1.ytf()) < 1e-6); + CHECK(cabs(param1.yft() - param1.yft()) < 1e-6); + } + + SUBCASE("Test transformer phase shift") { CHECK(transformer.phase_shift() == doctest::Approx(0.0)); } +} + +} // namespace power_grid_model \ No newline at end of file diff --git a/tests/unit/validation/test_input_validation.py b/tests/unit/validation/test_input_validation.py index 4594c6a9f..3aea93e71 100644 --- a/tests/unit/validation/test_input_validation.py +++ b/tests/unit/validation/test_input_validation.py @@ -435,6 +435,7 @@ def test_validate_input_data_sym_calculation(input_data): [ "node", "line", + "generic_branch", "transformer", "three_winding_transformer", "source", @@ -453,7 +454,7 @@ def test_validate_input_data_sym_calculation(input_data): "sym_power_sensor", "measured_object", [7, 10], - ["line", "transformer"], + ["line", "generic_branch", "transformer"], {"measured_terminal_type": MeasuredTerminalType.branch_to}, ) in validation_errors @@ -471,6 +472,7 @@ def test_validate_input_data_sym_calculation(input_data): [ "node", "line", + "generic_branch", "transformer", "three_winding_transformer", "source", @@ -489,7 +491,7 @@ def test_validate_input_data_sym_calculation(input_data): "asym_power_sensor", "measured_object", [7, 10], - ["line", "transformer"], + ["line", "generic_branch", "transformer"], {"measured_terminal_type": MeasuredTerminalType.branch_to}, ) in validation_errors diff --git a/tests/unit/validation/test_validation_functions.py b/tests/unit/validation/test_validation_functions.py index 2023cefbd..646ecf1f8 100644 --- a/tests/unit/validation/test_validation_functions.py +++ b/tests/unit/validation/test_validation_functions.py @@ -622,8 +622,8 @@ def test_validate_generic_power_sensor__all_terminal_types( @pytest.mark.parametrize( ("ref_component", "measured_terminal_type"), [ - (["line", "transformer"], MeasuredTerminalType.branch_from), - (["line", "transformer"], MeasuredTerminalType.branch_to), + (["line", "generic_branch", "transformer"], MeasuredTerminalType.branch_from), + (["line", "generic_branch", "transformer"], MeasuredTerminalType.branch_to), ("source", MeasuredTerminalType.source), ("shunt", MeasuredTerminalType.shunt), (["sym_load", "asym_load"], MeasuredTerminalType.load),