From 39f5ef3a62ccdff034b443e3f621fad84c745e20 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 28 Nov 2024 17:29:07 +0100 Subject: [PATCH 01/49] migrate indexer main model test to test api model Signed-off-by: Martijn Govers --- tests/native_api_tests/CMakeLists.txt | 1 + .../native_api_tests/test_api_model_misc.cpp | 1101 +++++++++++++++++ 2 files changed, 1102 insertions(+) create mode 100644 tests/native_api_tests/test_api_model_misc.cpp diff --git a/tests/native_api_tests/CMakeLists.txt b/tests/native_api_tests/CMakeLists.txt index f82dcd743..b09b4eb72 100644 --- a/tests/native_api_tests/CMakeLists.txt +++ b/tests/native_api_tests/CMakeLists.txt @@ -8,6 +8,7 @@ set(PROJECT_SOURCES "test_api_buffer.cpp" "test_api_model.cpp" "test_api_model_update.cpp" + "test_api_model_misc.cpp" "test_api_serialization.cpp" "test_api_utils.cpp" ) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp new file mode 100644 index 000000000..38a3c4de6 --- /dev/null +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -0,0 +1,1101 @@ +// SPDX-FileCopyrightText: Contributors to the Power Grid Model project +// +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include + +#include +#include +#include + +namespace power_grid_model_cpp { +namespace { +using std::numbers::pi; +using std::numbers::sqrt3; + +constexpr Idx default_option{-1}; +constexpr double nan = std::numeric_limits::quiet_NaN(); +constexpr double deg_120 = 2.0 / 3.0 * pi; +constexpr double deg_240 = 4.0 / 3.0 * pi; + +enum class CalculationSymmetry : Idx { symmetric = 0, asymmetric = 1 }; +enum class LoadGenType : IntS { + const_pq = 0, // constant power + const_y = 1, // constant element_admittance (impedance) + const_i = 2, // constant current +}; +enum class MeasuredTerminalType : IntS { + branch_from = 0, + branch_to = 1, + source = 2, + shunt = 3, + load = 4, + generator = 5, + branch3_1 = 6, + branch3_2 = 7, + branch3_3 = 8, + node = 9 +}; + +enum class FaultType : IntS { + // three_phase = 0, + single_phase_to_ground = 1, + // two_phase = 2, + // two_phase_to_ground = 3, + // nan = -128 +}; + +enum class FaultPhase : IntS { + // abc = 0, + a = 1, + // b = 2, + // c = 3, + // ab = 4, + // ac = 5, + // bc = 6, + // default_value = -1, + // nan = na_IntS +}; + +Options get_default_options(CalculationSymmetry calculation_symmetry, PGM_CalculationMethod calculation_method, + Idx threading = default_option) { + Options opt; + opt.set_calculation_type(PGM_power_flow); + opt.set_symmetric(static_cast(calculation_symmetry)); + opt.set_calculation_method(calculation_method); + if (threading != default_option) { + opt.set_threading(threading); + } + return opt; +} + +// struct regular_update { +// using update_type = permanent_update_t; +// }; + +// struct cached_update { +// using update_type = cached_update_t; +// }; + +namespace test { +constexpr double z_bus_2 = 1.0 / (0.015 + 0.5e6 / 10e3 / 10e3 * 2); +constexpr double z_total = z_bus_2 + 10.0; +constexpr double u1 = 1.05 * z_bus_2 / (z_bus_2 + 10.0); +constexpr double i = 1.05 * 10e3 / z_total / sqrt3; +constexpr double i_shunt = 0.015 / 0.025 * i; +constexpr double i_load = 0.005 / 0.025 * i; +} // namespace test + +struct State { + std::vector node_id{1, 2, 3}; + std::vector node_u_rated{10e3, 10e3, 10e3}; + + std::vector line_id{4}; + std::vector line_from_node{1}; + std::vector line_to_node{2}; + std::vector line_from_status{1}; + std::vector line_to_status{1}; + std::vector line_r1{10.0}; + std::vector line_x1{0.0}; + std::vector line_c1{0.0}; + std::vector line_tan1{0.0}; + std::vector line_r0{10.0}; + std::vector line_x0{0.0}; + std::vector line_c0{0.0}; + std::vector line_tan0{0.0}; + std::vector line_i_n{1e3}; + + std::vector link_id{5}; + std::vector link_from_node{2}; + std::vector link_to_node{3}; + std::vector link_from_status{1}; + std::vector link_to_status{1}; + + std::vector source_id{6, 10}; + std::vector source_node{1, 3}; + std::vector source_status{1, 0}; + std::vector source_u_ref{1.05, 1.05}; + std::vector source_u_ref_angle{nan, 0.0}; + std::vector source_sk{1e12, 1e12}; + + std::vector sym_load_id{7}; + std::vector sym_load_node{3}; + std::vector sym_load_status{1}; + std::vector sym_load_type{LoadGenType::const_y}; + std::vector sym_load_p_specified{0.5e6}; + std::vector sym_load_q_specified{0.0}; + + std::vector asym_load_id{8}; + std::vector asym_load_node{3}; + std::vector asym_load_status{1}; + std::vector asym_load_type{LoadGenType::const_y}; + std::vector asym_load_p_specified{0.5e6 / 3.0, 0.5e6 / 3.0, 0.5e6 / 3.0}; + std::vector asym_load_q_specified{0.0, 0.0, 0.0}; + + std::vector shunt_id{9}; + std::vector shunt_node{3}; + std::vector shunt_status{1}; + std::vector shunt_g1{0.015}; + std::vector shunt_b1{0.0}; + std::vector shunt_g0{0.015}; + std::vector shunt_b0{0.0}; + + std::vector sym_power_sensor_id{11, 13, 14, 15, 16, 17, 28}; + std::vector sym_power_sensor_measured_object{4, 6, 6, 9, 7, 8, 3}; + std::vector sym_power_sensor_measured_terminal_type{ + MeasuredTerminalType::branch_from, MeasuredTerminalType::source, MeasuredTerminalType::source, + MeasuredTerminalType::shunt, MeasuredTerminalType::load, MeasuredTerminalType::load, + MeasuredTerminalType::node}; + std::vector sym_power_sensor_power_sigma{0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02}; + std::vector sym_power_sensor_p_measured{1.1e6, 1.3e6, 1.4e6, 1.5e6, 1.6e6, 1.7e6, 3.0e6}; + std::vector sym_power_sensor_q_measured{1.1e3, 1.3e3, 1.4e3, 1.5e3, 1.6e3, 1.7e3, 3.0e3}; + + std::vector asym_power_sensor_id{18, 20, 21, 22, 23, 24, 29}; + std::vector asym_power_sensor_measured_object{4, 6, 6, 9, 7, 8, 3}; + std::vector asym_power_sensor_measured_terminal_type{ + MeasuredTerminalType::branch_from, MeasuredTerminalType::source, MeasuredTerminalType::source, + MeasuredTerminalType::shunt, MeasuredTerminalType::load, MeasuredTerminalType::load, + MeasuredTerminalType::node}; + std::vector asym_power_sensor_power_sigma{0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02}; + std::vector asym_power_sensor_p_measured{ + 2.11e6, 2.12e6, 2.13e6, // 18 + 2.31e6, 2.32e6, 2.33e6, // 20 + 2.41e6, 2.42e6, 2.43e6, // 21 + 2.51e6, 2.52e6, 2.53e6, // 22 + 2.61e6, 2.62e6, 2.63e6, // 23 + 2.71e6, 2.72e6, 2.73e6, // 24 + 5.01e6, 5.02e6, 5.03e6 // 28 + }; + std::vector asym_power_sensor_q_measured{ + 2.11e3, 2.12e3, 2.13e3, // 18 + 2.31e3, 2.32e3, 2.33e3, // 20 + 2.41e3, 2.42e3, 2.43e3, // 21 + 2.51e3, 2.52e3, 2.53e3, // 22 + 2.61e3, 2.62e3, 2.63e3, // 23 + 2.71e3, 2.72e3, 2.73e3, // 24 + 5.01e3, 5.02e3, 5.03e3, // 29 + }; + + std::vector sym_voltage_sensor_id{25, 26}; + std::vector sym_voltage_sensor_measured_object{1, 2}; + std::vector sym_voltage_sensor_u_sigma{105.0, 105.0}; + std::vector sym_voltage_sensor_u_measured{10.1e3, 10.2e3}; + std::vector sym_voltage_sensor_u_angle_measured{0.1, 0.2}; + + std::vector asym_voltage_sensor_id{27}; + std::vector asym_voltage_sensor_measured_object{3}; + std::vector asym_voltage_sensor_u_sigma{105.0}; + std::vector asym_voltage_sensor_u_measured{10.31e3 / sqrt3, 10.32e3 / sqrt3, 10.33e3 / sqrt3}; + std::vector asym_voltage_sensor_u_angle_measured{0.0, -deg_120, -deg_240}; + + std::vector fault_id{30}; + std::vector fault_status{1}; + std::vector fault_type{FaultType::single_phase_to_ground}; + std::vector fault_phase{FaultPhase::a}; + std::vector fault_object{3}; + std::vector fault_r_f{0.1}; + std::vector fault_x_f{0.1}; + + auto get_input_dataset() const { + DatasetConst result{"input", 0, 1}; + + result.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + result.add_attribute_buffer("node", "id", node_id.data()); + result.add_attribute_buffer("node", "u_rated", node_u_rated.data()); + + result.add_buffer("line", line_id.size(), line_id.size(), nullptr, nullptr); + result.add_attribute_buffer("line", "id", line_id.data()); + result.add_attribute_buffer("line", "from_node", line_from_node.data()); + result.add_attribute_buffer("line", "to_node", line_to_node.data()); + result.add_attribute_buffer("line", "from_status", line_from_status.data()); + result.add_attribute_buffer("line", "to_status", line_to_status.data()); + result.add_attribute_buffer("line", "r1", line_r1.data()); + result.add_attribute_buffer("line", "x1", line_x1.data()); + result.add_attribute_buffer("line", "c1", line_c1.data()); + result.add_attribute_buffer("line", "tan1", line_tan1.data()); + result.add_attribute_buffer("line", "r0", line_r0.data()); + result.add_attribute_buffer("line", "x0", line_x0.data()); + result.add_attribute_buffer("line", "c0", line_c0.data()); + result.add_attribute_buffer("line", "tan0", line_tan0.data()); + result.add_attribute_buffer("line", "i_n", line_i_n.data()); + + result.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); + result.add_attribute_buffer("link", "id", link_id.data()); + result.add_attribute_buffer("link", "from_node", link_from_node.data()); + result.add_attribute_buffer("link", "to_node", link_to_node.data()); + result.add_attribute_buffer("link", "from_status", link_from_status.data()); + result.add_attribute_buffer("link", "to_status", link_to_status.data()); + + result.add_buffer("source", source_id.size(), source_id.size(), nullptr, nullptr); + result.add_attribute_buffer("source", "id", source_id.data()); + result.add_attribute_buffer("source", "node", source_node.data()); + result.add_attribute_buffer("source", "status", source_status.data()); + result.add_attribute_buffer("source", "u_ref", source_u_ref.data()); + result.add_attribute_buffer("source", "u_ref_angle", source_u_ref_angle.data()); + result.add_attribute_buffer("source", "sk", source_sk.data()); + + result.add_buffer("sym_load", sym_load_id.size(), sym_load_id.size(), nullptr, nullptr); + result.add_attribute_buffer("sym_load", "id", sym_load_id.data()); + result.add_attribute_buffer("sym_load", "node", sym_load_node.data()); + result.add_attribute_buffer("sym_load", "status", sym_load_status.data()); + result.add_attribute_buffer("sym_load", "type", sym_load_type.data()); + result.add_attribute_buffer("sym_load", "p_specified", sym_load_p_specified.data()); + result.add_attribute_buffer("sym_load", "q_specified", sym_load_q_specified.data()); + + result.add_buffer("asym_load", asym_load_id.size(), asym_load_id.size(), nullptr, nullptr); + result.add_attribute_buffer("asym_load", "id", asym_load_id.data()); + result.add_attribute_buffer("asym_load", "node", asym_load_node.data()); + result.add_attribute_buffer("asym_load", "status", asym_load_status.data()); + result.add_attribute_buffer("asym_load", "type", asym_load_type.data()); + result.add_attribute_buffer("asym_load", "p_specified", asym_load_p_specified.data()); + result.add_attribute_buffer("asym_load", "q_specified", asym_load_q_specified.data()); + + result.add_buffer("shunt", shunt_id.size(), shunt_id.size(), nullptr, nullptr); + result.add_attribute_buffer("shunt", "id", shunt_id.data()); + result.add_attribute_buffer("shunt", "node", shunt_node.data()); + result.add_attribute_buffer("shunt", "status", shunt_status.data()); + result.add_attribute_buffer("shunt", "g1", shunt_g1.data()); + result.add_attribute_buffer("shunt", "b1", shunt_b1.data()); + result.add_attribute_buffer("shunt", "g0", shunt_g0.data()); + result.add_attribute_buffer("shunt", "b0", shunt_b0.data()); + + result.add_buffer("sym_power_sensor", sym_power_sensor_id.size(), sym_power_sensor_id.size(), nullptr, nullptr); + result.add_attribute_buffer("sym_power_sensor", "id", sym_power_sensor_id.data()); + result.add_attribute_buffer("sym_power_sensor", "measured_object", sym_power_sensor_measured_object.data()); + result.add_attribute_buffer("sym_power_sensor", "measured_terminal_type", + sym_power_sensor_measured_terminal_type.data()); + result.add_attribute_buffer("sym_power_sensor", "power_sigma", sym_power_sensor_power_sigma.data()); + result.add_attribute_buffer("sym_power_sensor", "p_measured", sym_power_sensor_p_measured.data()); + result.add_attribute_buffer("sym_power_sensor", "q_measured", sym_power_sensor_q_measured.data()); + + result.add_buffer("asym_power_sensor", asym_power_sensor_id.size(), asym_power_sensor_id.size(), nullptr, + nullptr); + result.add_attribute_buffer("asym_power_sensor", "id", asym_power_sensor_id.data()); + result.add_attribute_buffer("asym_power_sensor", "measured_object", asym_power_sensor_measured_object.data()); + result.add_attribute_buffer("asym_power_sensor", "measured_terminal_type", + asym_power_sensor_measured_terminal_type.data()); + result.add_attribute_buffer("asym_power_sensor", "power_sigma", asym_power_sensor_power_sigma.data()); + result.add_attribute_buffer("asym_power_sensor", "p_measured", asym_power_sensor_p_measured.data()); + result.add_attribute_buffer("asym_power_sensor", "q_measured", asym_power_sensor_q_measured.data()); + + result.add_buffer("sym_voltage_sensor", sym_voltage_sensor_id.size(), sym_voltage_sensor_id.size(), nullptr, + nullptr); + result.add_attribute_buffer("sym_voltage_sensor", "id", sym_voltage_sensor_id.data()); + result.add_attribute_buffer("sym_voltage_sensor", "measured_object", sym_voltage_sensor_measured_object.data()); + result.add_attribute_buffer("sym_voltage_sensor", "u_sigma", sym_voltage_sensor_u_sigma.data()); + result.add_attribute_buffer("sym_voltage_sensor", "u_measured", sym_voltage_sensor_u_measured.data()); + result.add_attribute_buffer("sym_voltage_sensor", "u_angle_measured", + sym_voltage_sensor_u_angle_measured.data()); + + result.add_buffer("asym_voltage_sensor", asym_voltage_sensor_id.size(), asym_voltage_sensor_id.size(), nullptr, + nullptr); + result.add_attribute_buffer("asym_voltage_sensor", "id", asym_voltage_sensor_id.data()); + result.add_attribute_buffer("asym_voltage_sensor", "measured_object", + asym_voltage_sensor_measured_object.data()); + result.add_attribute_buffer("asym_voltage_sensor", "u_sigma", asym_voltage_sensor_u_sigma.data()); + result.add_attribute_buffer("asym_voltage_sensor", "u_measured", asym_voltage_sensor_u_measured.data()); + result.add_attribute_buffer("asym_voltage_sensor", "u_angle_measured", + asym_voltage_sensor_u_angle_measured.data()); + + result.add_buffer("fault", fault_id.size(), fault_id.size(), nullptr, nullptr); + result.add_attribute_buffer("fault", "id", fault_id.data()); + result.add_attribute_buffer("fault", "status", fault_status.data()); + result.add_attribute_buffer("fault", "fault_type", fault_type.data()); + result.add_attribute_buffer("fault", "fault_phase", fault_phase.data()); + result.add_attribute_buffer("fault", "fault_object", fault_object.data()); + result.add_attribute_buffer("fault", "r_f", fault_r_f.data()); + result.add_attribute_buffer("fault", "x_f", fault_x_f.data()); + + return result; + } + + // // output vector + // std::vector> sym_node = std::vector>(3); + // std::vector> sym_branch = std::vector>(2); + // std::vector> sym_appliance = std::vector>(5); + // std::vector> asym_node = std::vector>(3); + // std::vector> asym_branch = std::vector>(2); + // std::vector> asym_appliance = std::vector>(5); + + // // individual symmetric + // std::vector> sym_line = std::vector>(1); + // std::vector> sym_link = std::vector>(1); + // std::vector> sym_load_sym = std::vector>(1); + // std::vector> sym_load_asym = std::vector>(1); + // std::vector> sym_source = std::vector>(2); + // std::vector> sym_shunt = std::vector>(1); + // std::vector> sym_voltage_sensor_sensor = + // std::vector>(2); std::vector> + // asym_voltage_sensor_sensor_sym_output = + // std::vector>(1); + // std::vector> sym_power_sensor = std::vector>(7); + // std::vector> asym_power_sensor_sym_output = + // std::vector>(7); + + // // individual asymmetric + // std::vector> asym_line = std::vector>(1); + // std::vector> asym_link = std::vector>(1); + // std::vector> asym_load_sym = std::vector>(1); + // std::vector> asym_load_asym = std::vector>(1); + // std::vector> asym_source = std::vector>(2); + // std::vector> asym_shunt = std::vector>(1); + // std::vector> asym_voltage_sensor_sensor = + // std::vector>(1); + // std::vector> sym_voltage_sensor_sensor_asym_output = + // std::vector>(2); + // std::vector> asym_power_sensor = std::vector>(7); + // std::vector> sym_power_sensor_asym_output = + // std::vector>(7); + + // // update vector + // std::vector sym_load_update{{7, 1, 1.0e6, nan}}; + // std::vector asym_load_update{{8, 0, RealValue{nan}, + // RealValue{nan}}}; std::vector shunt_update{{9, 0, nan, 0.02, nan, 0.02}}; + // std::vector shunt_update_2{{6, 0, nan, 0.01, nan, 0.01}}; // used for test case alternate compute + // mode std::vector source_update{{10, 1, test::u1, nan}}; std::vector link_update{{5, + // 1, 0}}; std::vector fault_update{{30, 1, FaultType::three_phase, FaultPhase::abc, 1, nan, nan}}; + + // // batch update vector + // std::vector batch_sym_load_update{{7, 1, 1.0e6, nan}, {7}, {7}, {7}, {7}}; + // std::vector batch_asym_load_update{ + // {8, 0, RealValue{nan}, RealValue{nan}}, {8}, {8}, {8}, {8}}; + // std::vector batch_shunt_update{{9, 0, nan, 0.02, nan, 0.02}, {9}, {9}, {9}, {9}}; + // std::vector batch_source_update{{10, 1, test::u1, nan}, {10}, {10}, {10}, {10}}; + // std::vector batch_link_update{{5, 1, 0}, {5}, {5}, {5}, {5}}; + // std::vector batch_fault_update{ + // {30, 1, FaultType::three_phase, FaultPhase::abc, 1, nan, nan}, {30}, {30}, {30}, {30}}; +}; + +auto default_model(State const& state) -> Model { return Model{50.0, state.get_input_dataset()}; } +} // namespace + +TEST_CASE("API Model - misc") { + State state; + auto model = default_model(state); + + SUBCASE("Test get indexer") { // TODO(mgovers): needed + std::vector const node_id{2, 1, 3, 2}; + std::vector const expected_indexer{1, 0, 2, 1}; + std::vector indexer(node_id.size()); + model.get_indexer("node", node_id.size(), node_id.data(), indexer.data()); + CHECK(indexer == expected_indexer); + } + + // SUBCASE("Test duplicated id") { // TODO(mgovers): needed; captured in Python test; maybe move to + // // test_main_core_input.cpp + // MainModel model2{50.0, meta_data::meta_data_gen::meta_data}; + // state.node_input[1].id = 1; + // CHECK_THROWS_AS(model2.add_component(state.node_input), ConflictID); + // } + + // SUBCASE("Test no existing id") { // TODO(mgovers): needed; captured in Python test; maybe move to + // // test_main_core_input.cpp + // MainModel model2{50.0, meta_data::meta_data_gen::meta_data}; + // state.line_input[0].from_node = 100; + // model2.add_component(state.node_input); + // CHECK_THROWS_AS(model2.add_component(state.line_input), IDNotFound); + // } + + // SUBCASE("Test id for wrong type") { // TODO(mgovers): needed; captured in Python test but not all flavors; maybe + // // move to test_main_core_input.cpp + // MainModel model2{50.0, meta_data::meta_data_gen::meta_data}; + + // state.link_input[0].from_node = 4; + // model2.add_component(state.node_input); // 1 2 3 + // model2.add_component(state.line_input); // 4 + // CHECK_THROWS_AS(model2.add_component(state.link_input), IDWrongType); + + // // Fix link input, retry + // state.link_input[0].from_node = 2; + // model2.add_component(state.link_input); // 5 + + // model2.add_component(state.source_input); // 6 10 + // model2.add_component(state.sym_load_input); // 7 + // model2.add_component(state.asym_load_input); // 8 + // model2.add_component(state.shunt_input); // 9 + + // // voltage sensor with a measured id which is not a node (link) + // state.sym_voltage_sensor_sensor_input[0].measured_object = 5; + // CHECK_THROWS_AS(model2.add_component(state.sym_voltage_sensor_sensor_input), IDWrongType); + + // // Test for all MeasuredTerminalType instances + // using enum MeasuredTerminalType; + // std::vector const mt_types{branch_from, branch_to, generator, load, shunt, source}; + + // // power sensor with terminal branch, with a measured id which is not a branch (node) + // for (auto const& mt_type : mt_types) { + // state.sym_power_sensor_input[0].measured_object = 1; + // state.sym_power_sensor_input[0].measured_terminal_type = mt_type; + // CHECK_THROWS_AS(model2.add_component(state.sym_power_sensor_input), IDWrongType); + // } + // } +} + +// TEST_CASE_TEMPLATE("Test main model - unknown id", settings, regular_update, +// cached_update) { // TODO(mgovers): we need this test +// State const state; +// auto model = default_model(state); + +// std::vector const source_update2{SourceUpdate{100, true, nan, nan}}; +// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; +// update_data.add_buffer("source", source_update2.size(), source_update2.size(), nullptr, source_update2.data()); +// CHECK_THROWS_AS((model.update_components(update_data)), IDNotFound); +// } + +// TEST_CASE_TEMPLATE( +// "Test main model - update only load", settings, regular_update, +// cached_update) { // TODO(mgovers): we should whitebox-test this instead; values not reproduced by validation +// tests State state; auto model = default_model(state); + +// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; +// update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, +// state.sym_load_update.data()); +// update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, +// state.asym_load_update.data()); +// model.update_components(update_data); + +// SUBCASE("Symmetrical") { +// auto const solver_output = +// model.calculate(get_default_options(symmetric, +// CalculationMethod::linear)); +// model.output_result(solver_output, state.sym_node); +// model.output_result(solver_output, state.sym_branch); +// model.output_result(solver_output, state.sym_appliance); +// CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); +// CHECK(state.sym_node[1].u_pu == doctest::Approx(test::u1)); +// CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); +// CHECK(state.sym_branch[0].i_from == doctest::Approx(test::i)); +// CHECK(state.sym_appliance[0].i == doctest::Approx(test::i)); +// CHECK(state.sym_appliance[1].i == doctest::Approx(0.0)); +// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load * 2)); +// CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); +// CHECK(state.sym_appliance[4].i == doctest::Approx(test::i_shunt)); +// } +// SUBCASE("Asymmetrical") { +// auto const solver_output = model.calculate( +// get_default_options(asymmetric, CalculationMethod::linear)); +// model.output_result(solver_output, state.asym_node); +// model.output_result(solver_output, state.asym_branch); +// model.output_result(solver_output, state.asym_appliance); +// CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); +// CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); +// CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); +// CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(test::i)); +// CHECK(state.asym_appliance[0].i(1) == doctest::Approx(test::i)); +// CHECK(state.asym_appliance[1].i(2) == doctest::Approx(0.0)); +// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2)); +// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); +// CHECK(state.asym_appliance[4].i(2) == doctest::Approx(test::i_shunt)); +// } +// } + +// TEST_CASE_TEMPLATE( +// "Test main model - update load and shunt param", settings, regular_update, +// cached_update) { // TODO(mgovers): we should whitebox-test this instead; values not reproduced by validation +// tests State state; auto model = default_model(state); + +// state.sym_load_update[0].p_specified = 2.5e6; +// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; +// update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, +// state.sym_load_update.data()); +// update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, +// state.asym_load_update.data()); +// update_data.add_buffer("shunt", state.shunt_update.size(), state.shunt_update.size(), nullptr, +// state.shunt_update.data()); +// model.update_components(update_data); + +// SUBCASE("Symmetrical") { +// auto const solver_output = +// model.calculate(get_default_options(symmetric, +// CalculationMethod::linear)); +// model.output_result(solver_output, state.sym_node); +// model.output_result(solver_output, state.sym_branch); +// model.output_result(solver_output, state.sym_appliance); +// CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); +// CHECK(state.sym_node[1].u_pu == doctest::Approx(test::u1)); +// CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); +// CHECK(state.sym_branch[0].i_from == doctest::Approx(test::i)); +// CHECK(state.sym_appliance[0].i == doctest::Approx(test::i)); +// CHECK(state.sym_appliance[1].i == doctest::Approx(0.0)); +// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load * 2 + test::i_shunt)); +// CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); +// CHECK(state.sym_appliance[4].i == doctest::Approx(0.0)); +// } +// SUBCASE("Asymmetrical") { +// auto const solver_output = model.calculate( +// get_default_options(asymmetric, CalculationMethod::linear)); +// model.output_result(solver_output, state.asym_node); +// model.output_result(solver_output, state.asym_branch); +// model.output_result(solver_output, state.asym_appliance); +// CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); +// CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); +// CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); +// CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(test::i)); +// CHECK(state.asym_appliance[0].i(1) == doctest::Approx(test::i)); +// CHECK(state.asym_appliance[1].i(2) == doctest::Approx(0.0)); +// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2 + test::i_shunt)); +// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); +// CHECK(state.asym_appliance[4].i(2) == doctest::Approx(0.0)); +// } +// } + +// TEST_CASE_TEMPLATE( +// "Test main model - all updates", settings, regular_update, +// cached_update) { // TODO(mgovers): we should whitebox-test this instead; values not reproduced by validation +// tests State state; auto model = default_model(state); + +// state.sym_load_update[0].p_specified = 2.5e6; +// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; +// update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, +// state.sym_load_update.data()); +// update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, +// state.asym_load_update.data()); +// update_data.add_buffer("shunt", state.shunt_update.size(), state.shunt_update.size(), nullptr, +// state.shunt_update.data()); +// update_data.add_buffer("source", state.source_update.size(), state.source_update.size(), nullptr, +// state.source_update.data()); +// update_data.add_buffer("link", state.link_update.size(), state.link_update.size(), nullptr, +// state.link_update.data()); +// update_data.add_buffer("fault", state.fault_update.size(), state.fault_update.size(), nullptr, +// state.fault_update.data()); + +// model.update_components(update_data); + +// SUBCASE("Symmetrical") { +// auto const solver_output = +// model.calculate(get_default_options(symmetric, +// CalculationMethod::linear)); +// model.output_result(solver_output, state.sym_node); +// model.output_result(solver_output, state.sym_branch); +// model.output_result(solver_output, state.sym_appliance); +// CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); +// CHECK(state.sym_node[1].u_pu == doctest::Approx(1.05)); +// CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); +// CHECK(state.sym_branch[0].i_from == doctest::Approx(0.0).epsilon(1e-6)); +// CHECK(state.sym_appliance[0].i == doctest::Approx(0.0).epsilon(1e-6)); +// CHECK(state.sym_appliance[1].i == doctest::Approx(test::i)); +// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i)); +// CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); +// CHECK(state.sym_appliance[4].i == doctest::Approx(0.0)); +// } +// SUBCASE("Asymmetrical") { +// auto const solver_output = model.calculate( +// get_default_options(asymmetric, CalculationMethod::linear)); +// model.output_result(solver_output, state.asym_node); +// model.output_result(solver_output, state.asym_branch); +// model.output_result(solver_output, state.asym_appliance); +// CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); +// CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(1.05)); +// CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); +// CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(0.0).epsilon(1e-6)); +// CHECK(state.asym_appliance[0].i(1) == doctest::Approx(0.0).epsilon(1e-6)); +// CHECK(state.asym_appliance[1].i(2) == doctest::Approx(test::i)); +// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i)); +// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); +// CHECK(state.asym_appliance[4].i(2) == doctest::Approx(0.0)); +// } +// } + +// TEST_CASE_TEMPLATE("Test main model - single permanent update from batch", settings, regular_update, +// cached_update) { // TODO(mgovers): we should whitebox-test this instead +// State state; +// auto model = default_model(state); + +// state.batch_sym_load_update[0].p_specified = 2.5e6; +// ConstDataset update_data{true, 5, "update", meta_data::meta_data_gen::meta_data}; +// update_data.add_buffer("sym_load", 1, state.batch_sym_load_update.size(), nullptr, +// state.batch_sym_load_update.data()); +// update_data.add_buffer("asym_load", 1, state.batch_asym_load_update.size(), nullptr, +// state.batch_asym_load_update.data()); +// update_data.add_buffer("shunt", 1, state.batch_shunt_update.size(), nullptr, state.batch_shunt_update.data()); +// update_data.add_buffer("source", 1, state.batch_source_update.size(), nullptr, state.batch_source_update.data()); +// update_data.add_buffer("link", 1, state.batch_link_update.size(), nullptr, state.batch_link_update.data()); +// update_data.add_buffer("fault", 1, state.batch_fault_update.size(), nullptr, state.batch_fault_update.data()); + +// model.update_components(update_data); + +// SUBCASE("Symmetrical") { +// auto const solver_output = +// model.calculate(get_default_options(symmetric, +// CalculationMethod::linear)); +// model.output_result(solver_output, state.sym_node); +// model.output_result(solver_output, state.sym_branch); +// model.output_result(solver_output, state.sym_appliance); +// CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); +// CHECK(state.sym_node[1].u_pu == doctest::Approx(1.05)); +// CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); +// CHECK(state.sym_branch[0].i_from == doctest::Approx(0.0).epsilon(1e-6)); +// CHECK(state.sym_appliance[0].i == doctest::Approx(0.0).epsilon(1e-6)); +// CHECK(state.sym_appliance[1].i == doctest::Approx(test::i)); +// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i)); +// CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); +// CHECK(state.sym_appliance[4].i == doctest::Approx(0.0)); +// } +// SUBCASE("Asymmetrical") { +// auto const solver_output = model.calculate( +// get_default_options(asymmetric, CalculationMethod::linear)); +// model.output_result(solver_output, state.asym_node); +// model.output_result(solver_output, state.asym_branch); +// model.output_result(solver_output, state.asym_appliance); +// CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); +// CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(1.05)); +// CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); +// CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(0.0).epsilon(1e-6)); +// CHECK(state.asym_appliance[0].i(1) == doctest::Approx(0.0).epsilon(1e-6)); +// CHECK(state.asym_appliance[1].i(2) == doctest::Approx(test::i)); +// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i)); +// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); +// CHECK(state.asym_appliance[4].i(2) == doctest::Approx(0.0)); +// } +// } + +// TEST_CASE_TEMPLATE("Test main model - restore components", settings, regular_update, +// cached_update) { // TODO(mgovers): either whitebox (as a sub-part of batch impl) or otherwise drop +// // entirely (tested by batch update) +// State state; +// auto model = default_model(state); + +// auto const solver_output_orig = +// model.calculate(get_default_options(symmetric, CalculationMethod::linear)); + +// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; +// update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, +// state.sym_load_update.data()); +// update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, +// state.asym_load_update.data()); + +// model.update_components(update_data); +// model.restore_components(update_data); + +// SUBCASE("Symmetrical") { +// auto const solver_output_result = +// model.calculate(get_default_options(symmetric, +// CalculationMethod::linear)); +// model.output_result(solver_output_result, state.sym_node); +// model.output_result(solver_output_result, state.sym_branch); +// model.output_result(solver_output_result, state.sym_appliance); + +// CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); +// CHECK(state.sym_node[1].u_pu == doctest::Approx(test::u1)); +// CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); +// CHECK(state.sym_branch[0].i_from == doctest::Approx(test::i)); +// CHECK(state.sym_appliance[0].i == doctest::Approx(test::i)); +// CHECK(state.sym_appliance[1].i == doctest::Approx(0.0)); +// if constexpr (settings::update_type::value) { +// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load)); +// CHECK(state.sym_appliance[3].i == doctest::Approx(test::i_load)); +// } else { +// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load * 2)); +// CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); +// } +// CHECK(state.sym_appliance[4].i == doctest::Approx(test::i_shunt)); +// } +// SUBCASE("Asymmetrical") { +// auto const solver_output = model.calculate( +// get_default_options(asymmetric, CalculationMethod::linear)); +// model.output_result(solver_output, state.asym_node); +// model.output_result(solver_output, state.asym_branch); +// model.output_result(solver_output, state.asym_appliance); + +// CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); +// CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); +// CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); +// CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(test::i)); +// CHECK(state.asym_appliance[0].i(1) == doctest::Approx(test::i)); +// CHECK(state.asym_appliance[1].i(2) == doctest::Approx(0.0)); +// if constexpr (settings::update_type::value) { +// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load)); +// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(test::i_load)); +// } else { +// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2)); +// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); +// } +// CHECK(state.asym_appliance[4].i(2) == doctest::Approx(test::i_shunt)); +// } +// } + +// TEST_CASE_TEMPLATE("Test main model - updates w/ alternating compute mode", settings, regular_update, +// cached_update) { // TODO(mgovers): move to api tests; not possible with current validation +// framework +// constexpr auto check_sym = [](MainModel const& model_, auto const& math_output_) { +// State state_; +// model_.output_result(math_output_, state_.sym_node); +// model_.output_result(math_output_, state_.sym_branch); +// model_.output_result(math_output_, state_.sym_appliance); + +// CHECK(state_.sym_node[0].u_pu == doctest::Approx(1.05)); +// CHECK(state_.sym_node[1].u_pu == doctest::Approx(test::u1)); +// CHECK(state_.sym_node[2].u_pu == doctest::Approx(test::u1)); +// CHECK(state_.sym_branch[0].i_from == doctest::Approx(test::i)); +// CHECK(state_.sym_appliance[0].i == doctest::Approx(test::i)); +// CHECK(state_.sym_appliance[1].i == doctest::Approx(0.0)); +// CHECK(state_.sym_appliance[2].i == doctest::Approx(test::i_load * 2 + test::i_shunt)); +// CHECK(state_.sym_appliance[3].i == doctest::Approx(0.0)); +// CHECK(state_.sym_appliance[4].i == doctest::Approx(0.0)); +// }; +// constexpr auto check_asym = [](MainModel const& model_, auto const& math_output_) { +// State state_; +// model_.output_result(math_output_, state_.asym_node); +// model_.output_result(math_output_, state_.asym_branch); +// model_.output_result(math_output_, state_.asym_appliance); +// CHECK(state_.asym_node[0].u_pu(0) == doctest::Approx(1.05)); +// CHECK(state_.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); +// CHECK(state_.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); +// CHECK(state_.asym_branch[0].i_from(0) == doctest::Approx(test::i)); +// CHECK(state_.asym_appliance[0].i(1) == doctest::Approx(test::i)); +// CHECK(state_.asym_appliance[1].i(2) == doctest::Approx(0.0)); +// CHECK(state_.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2 + test::i_shunt)); +// CHECK(state_.asym_appliance[3].i(1) == doctest::Approx(0.0)); +// CHECK(state_.asym_appliance[4].i(2) == doctest::Approx(0.0)); +// }; + +// State state; +// auto model = default_model(state); + +// state.sym_load_update[0].p_specified = 2.5e6; + +// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; +// update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, +// state.sym_load_update.data()); +// update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, +// state.asym_load_update.data()); +// update_data.add_buffer("shunt", state.shunt_update.size(), state.shunt_update.size(), nullptr, +// state.shunt_update.data()); + +// // This will lead to no topo change but param change +// model.update_components(update_data); + +// auto const math_output_sym_1 = +// model.calculate(get_default_options(symmetric, CalculationMethod::linear)); +// check_sym(model, math_output_sym_1); + +// auto const math_output_asym_1 = +// model.calculate(get_default_options(asymmetric, CalculationMethod::linear)); +// check_asym(model, math_output_asym_1); + +// SUBCASE("No new update") { +// // Math state may be fully cached +// } +// if constexpr (std::same_as) { +// SUBCASE("No new parameter change") { +// // Math state may be fully cached due to no change +// model.update_components(update_data); +// } +// } +// SUBCASE("With parameter change") { +// // Restore to original state and re-apply same update: causes param change for cached update +// model.restore_components(update_data); +// model.update_components(update_data); +// } + +// auto const math_output_asym_2 = +// model.calculate(get_default_options(asymmetric, CalculationMethod::linear)); +// check_asym(model, math_output_asym_2); + +// auto const math_output_sym_2 = +// model.calculate(get_default_options(symmetric, CalculationMethod::linear)); +// check_sym(model, math_output_sym_2); + +// model.restore_components(update_data); +// } + +// namespace { +// auto incomplete_input_model(State const& state) -> MainModel { +// MainModel model{50.0, meta_data::meta_data_gen::meta_data}; + +// std::vector const incomplete_source_input{{6, 1, 1, nan, nan, 1e12, nan, nan}, +// {10, 3, 1, nan, nan, 1e12, nan, nan}}; +// std::vector const incomplete_sym_load_input{{7, 3, 1, LoadGenType::const_y, nan, 0.0}}; +// std::vector const incomplete_asym_load_input{ +// {8, 3, 1, LoadGenType::const_y, RealValue{nan}, RealValue{0.0}}}; + +// model.add_component(state.node_input); +// model.add_component(state.line_input); +// model.add_component(state.link_input); +// model.add_component(incomplete_source_input); +// model.add_component(incomplete_sym_load_input); +// model.add_component(incomplete_asym_load_input); +// model.add_component(state.shunt_input); +// model.set_construction_complete(); + +// return model; +// } +// } // namespace + +// TEST_CASE("Test main model - incomplete input") { +// using CalculationMethod::iterative_current; +// using CalculationMethod::linear; +// using CalculationMethod::linear_current; +// using CalculationMethod::newton_raphson; + +// State const state; +// auto model = default_model(state); +// auto test_model = incomplete_input_model(state); + +// std::vector complete_source_update{{6, 1, 1.05, nan}, {10, 1, 1.05, 0}}; +// std::vector complete_sym_load_update{{7, 1, 0.5e6, nan}}; +// std::vector complete_asym_load_update{ +// {8, 1, RealValue{0.5e6 / 3.0}, RealValue{nan}}}; + +// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; +// update_data.add_buffer("source", complete_source_update.size(), complete_source_update.size(), nullptr, +// complete_source_update.data()); +// update_data.add_buffer("sym_load", complete_sym_load_update.size(), complete_sym_load_update.size(), nullptr, +// complete_sym_load_update.data()); +// update_data.add_buffer("asym_load", complete_asym_load_update.size(), complete_asym_load_update.size(), nullptr, +// complete_asym_load_update.data()); + +// std::vector incomplete_source_update{{6, na_IntS, nan, nan}, {10, na_IntS, nan, nan}}; +// std::vector incomplete_sym_load_update{{7, na_IntS, nan, nan}}; +// std::vector incomplete_asym_load_update{ +// {8, na_IntS, RealValue{nan}, RealValue{nan}}}; + +// ConstDataset incomplete_update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; +// incomplete_update_data.add_buffer("source", incomplete_source_update.size(), incomplete_source_update.size(), +// nullptr, incomplete_source_update.data()); +// incomplete_update_data.add_buffer("sym_load", incomplete_sym_load_update.size(), +// incomplete_sym_load_update.size(), +// nullptr, incomplete_sym_load_update.data()); +// incomplete_update_data.add_buffer("asym_load", incomplete_asym_load_update.size(), +// incomplete_asym_load_update.size(), nullptr, +// incomplete_asym_load_update.data()); + +// MainModel const ref_model{model}; + +// SUBCASE("Asymmetrical - Complete") { // TODO(mgovers): no validation case for asym exists +// MutableDataset test_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; +// MutableDataset ref_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; + +// std::vector> test_asym_node(state.asym_node.size()); +// std::vector> ref_asym_node(state.asym_node.size()); +// test_result_data.add_buffer("node", test_asym_node.size(), test_asym_node.size(), nullptr, +// test_asym_node.data()); +// ref_result_data.add_buffer("node", ref_asym_node.size(), ref_asym_node.size(), nullptr, +// ref_asym_node.data()); + +// SUBCASE("Test linear calculation") { +// test_model.calculate(get_default_options(asymmetric, linear), test_result_data, update_data); +// model.calculate(get_default_options(asymmetric, linear), ref_result_data, update_data); +// } + +// SUBCASE("Test linear current calculation") { +// test_model.calculate(get_default_options(asymmetric, linear_current), test_result_data, update_data); +// model.calculate(get_default_options(asymmetric, linear_current), ref_result_data, update_data); +// } + +// SUBCASE("Test iterative current calculation") { +// test_model.calculate(get_default_options(asymmetric, iterative_current), test_result_data, update_data); +// model.calculate(get_default_options(asymmetric, iterative_current), ref_result_data, update_data); +// } + +// SUBCASE("Test iterative Newton-Rhapson calculation") { +// test_model.calculate(get_default_options(asymmetric, newton_raphson), test_result_data, update_data); +// model.calculate(get_default_options(asymmetric, newton_raphson), ref_result_data, update_data); +// } + +// for (auto component_idx : {0, 1, 2}) { +// CAPTURE(component_idx); + +// for (auto phase_idx : {0, 1, 2}) { +// CAPTURE(phase_idx); + +// CHECK(test_asym_node[component_idx].u_pu(phase_idx) == +// doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx))); +// } +// } +// } + +// SUBCASE("Symmetrical - Incomplete") { // TODO(mgovers): not tested elsewhere; maybe test in API model? +// MutableDataset test_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; +// MutableDataset const ref_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; + +// std::vector> test_sym_node(state.sym_node.size()); +// test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, +// test_sym_node.data()); + +// SUBCASE("Target dataset") { +// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = symmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// test_result_data), +// SparseMatrixError); +// } +// SUBCASE("Empty update dataset") { +// ConstDataset const update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; + +// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = symmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// test_result_data, update_data), +// SparseMatrixError); +// } +// SUBCASE("Update dataset") { +// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = symmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// test_result_data, incomplete_update_data), +// BatchCalculationError); +// } +// } + +// SUBCASE("Asymmetrical - Incomplete") { // TODO(mgovers): not tested elsewhere; maybe test in API model? +// MutableDataset test_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; +// MutableDataset const ref_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; + +// std::vector> test_sym_node(state.sym_node.size()); +// test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, +// test_sym_node.data()); + +// SUBCASE("Target dataset") { +// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = asymmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// test_result_data), +// SparseMatrixError); +// } +// SUBCASE("Empty update dataset") { +// ConstDataset const update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; + +// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = asymmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// test_result_data, update_data), +// SparseMatrixError); +// } +// SUBCASE("Update dataset") { +// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = asymmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// test_result_data, incomplete_update_data), +// BatchCalculationError); +// } +// } +// } + +// TEST_CASE("Test main model - Incomplete followed by complete") { // TODO(mgovers): This tests the reset of 2 +// consecutive +// // batch scenarios and definitely needs to be tested +// using CalculationMethod::linear; + +// State const state; +// auto model = default_model(state); +// auto test_model = incomplete_input_model(state); + +// constexpr Idx batch_size = 2; + +// std::vector mixed_source_update{ +// {6, 1, nan, nan}, {10, 1, nan, nan}, {6, 1, 1.05, nan}, {10, 1, 1.05, 0}}; +// std::vector mixed_sym_load_update{{7, 1, nan, 1.0}, {7, 1, 0.5e6, nan}}; +// std::vector mixed_asym_load_update{ +// {8, 1, RealValue{nan}, RealValue{1.0}}, +// {8, 1, RealValue{0.5e6 / 3.0}, RealValue{nan}}}; + +// auto const source_indptr = IdxVector{0, 0, static_cast(mixed_source_update.size())}; + +// REQUIRE(source_indptr.size() == batch_size + 1); + +// ConstDataset mixed_update_data{true, batch_size, "update", meta_data::meta_data_gen::meta_data}; +// mixed_update_data.add_buffer("source", 2, 4, nullptr, mixed_source_update.data()); +// mixed_update_data.add_buffer("sym_load", 1, 2, nullptr, mixed_sym_load_update.data()); +// mixed_update_data.add_buffer("asym_load", 1, 2, nullptr, mixed_asym_load_update.data()); + +// ConstDataset second_scenario_update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; +// second_scenario_update_data.add_buffer("source", 2, 2, nullptr, mixed_source_update.data() + 2); +// second_scenario_update_data.add_buffer("sym_load", 1, 1, nullptr, mixed_sym_load_update.data() + 1); +// second_scenario_update_data.add_buffer("asym_load", 1, 1, nullptr, mixed_asym_load_update.data() + 1); + +// SUBCASE("Symmetrical") { +// MutableDataset test_result_data{true, batch_size, "sym_output", meta_data::meta_data_gen::meta_data}; +// MutableDataset ref_result_data{false, 1, "sym_output", meta_data::meta_data_gen::meta_data}; + +// std::vector> test_sym_node(batch_size * state.sym_node.size(), +// {na_IntID, na_IntS, nan, nan, nan, nan, nan}); +// std::vector> ref_sym_node(state.sym_node.size(), +// {na_IntID, na_IntS, nan, nan, nan, nan, nan}); +// test_result_data.add_buffer("node", state.sym_node.size(), test_sym_node.size(), nullptr, +// test_sym_node.data()); ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), nullptr, +// ref_sym_node.data()); + +// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = symmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// test_result_data, mixed_update_data), +// BatchCalculationError); +// model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = symmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// ref_result_data, second_scenario_update_data); + +// CHECK(is_nan(test_sym_node[0].u_pu)); +// CHECK(is_nan(test_sym_node[1].u_pu)); +// CHECK(is_nan(test_sym_node[2].u_pu)); +// CHECK(test_sym_node[state.sym_node.size() + 0].u_pu == doctest::Approx(ref_sym_node[0].u_pu)); +// CHECK(test_sym_node[state.sym_node.size() + 1].u_pu == doctest::Approx(ref_sym_node[1].u_pu)); +// CHECK(test_sym_node[state.sym_node.size() + 2].u_pu == doctest::Approx(ref_sym_node[2].u_pu)); +// } + +// SUBCASE("Asymmetrical") { +// MutableDataset test_result_data{true, batch_size, "asym_output", meta_data::meta_data_gen::meta_data}; +// MutableDataset ref_result_data{false, 1, "asym_output", meta_data::meta_data_gen::meta_data}; + +// std::vector> test_asym_node( +// batch_size * state.sym_node.size(), +// {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, +// RealValue{nan}, RealValue{nan}, RealValue{nan}}); +// std::vector> ref_asym_node( +// state.sym_node.size(), +// {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, +// RealValue{nan}, RealValue{nan}, RealValue{nan}}); +// test_result_data.add_buffer("node", state.sym_node.size(), test_asym_node.size(), nullptr, +// test_asym_node.data()); +// ref_result_data.add_buffer("node", ref_asym_node.size(), ref_asym_node.size(), nullptr, +// ref_asym_node.data()); + +// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = asymmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// test_result_data, mixed_update_data), +// BatchCalculationError); +// model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = asymmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// ref_result_data, second_scenario_update_data); + +// for (auto component_idx : {0, 1, 2}) { +// CAPTURE(component_idx); + +// CHECK(is_nan(test_asym_node[component_idx].u_pu)); + +// for (auto phase_idx : {0, 1, 2}) { +// CAPTURE(phase_idx); + +// CHECK(test_asym_node[state.asym_node.size() + component_idx].u_pu(phase_idx) == +// doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx))); +// } +// } +// } +// } + +} // namespace power_grid_model_cpp From 5e28a875c0abc82e9b2a8fac39372142e97d490f Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 28 Nov 2024 17:46:25 +0100 Subject: [PATCH 02/49] migrate duplicated id + non existing id checks to test api model Signed-off-by: Martijn Govers --- .../native_api_tests/test_api_model_misc.cpp | 68 ++++++++++++++----- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index 38a3c4de6..332610b9b 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -373,30 +373,62 @@ auto default_model(State const& state) -> Model { return Model{50.0, state.get_i TEST_CASE("API Model - misc") { State state; - auto model = default_model(state); - SUBCASE("Test get indexer") { // TODO(mgovers): needed - std::vector const node_id{2, 1, 3, 2}; + SUBCASE("Test get indexer") { + std::vector const node_id{1, 2, 3}; + std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; + + DatasetConst input_dataset{"input", 0, 1}; + input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("node", "id", node_id.data()); + input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); + + auto model = Model{50.0, input_dataset}; + + std::vector const ids_to_index{2, 1, 3, 2}; std::vector const expected_indexer{1, 0, 2, 1}; - std::vector indexer(node_id.size()); - model.get_indexer("node", node_id.size(), node_id.data(), indexer.data()); + std::vector indexer(ids_to_index.size()); + model.get_indexer("node", ids_to_index.size(), ids_to_index.data(), indexer.data()); CHECK(indexer == expected_indexer); } - // SUBCASE("Test duplicated id") { // TODO(mgovers): needed; captured in Python test; maybe move to - // // test_main_core_input.cpp - // MainModel model2{50.0, meta_data::meta_data_gen::meta_data}; - // state.node_input[1].id = 1; - // CHECK_THROWS_AS(model2.add_component(state.node_input), ConflictID); - // } + SUBCASE("Test duplicated id") { + std::vector node_id{1, 1, 3}; + DatasetConst input_dataset{"input", 0, 1}; - // SUBCASE("Test no existing id") { // TODO(mgovers): needed; captured in Python test; maybe move to - // // test_main_core_input.cpp - // MainModel model2{50.0, meta_data::meta_data_gen::meta_data}; - // state.line_input[0].from_node = 100; - // model2.add_component(state.node_input); - // CHECK_THROWS_AS(model2.add_component(state.line_input), IDNotFound); - // } + input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("node", "id", node_id.data()); + + auto construct_model = [&] { Model{50.0, input_dataset}; }; + CHECK_THROWS_WITH_AS(construct_model(), "Conflicting id detected: 1\n", PowerGridRegularError); + } + + SUBCASE("Test non-existing id") { + std::vector const node_id{1, 2, 3}; + std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; + + std::vector link_id{5}; + std::vector link_from_node{99}; + std::vector link_to_node{3}; + std::vector link_from_status{1}; + std::vector link_to_status{1}; + + DatasetConst input_dataset{"input", 0, 1}; + + input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("node", "id", node_id.data()); + input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); + + input_dataset.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("link", "id", link_id.data()); + input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); + input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); + input_dataset.add_attribute_buffer("link", "from_status", link_from_status.data()); + input_dataset.add_attribute_buffer("link", "to_status", link_to_status.data()); + + auto construct_model = [&] { Model{50.0, input_dataset}; }; + CHECK_THROWS_WITH_AS(construct_model(), "The id cannot be found: 99\n", PowerGridRegularError); + } // SUBCASE("Test id for wrong type") { // TODO(mgovers): needed; captured in Python test but not all flavors; maybe // // move to test_main_core_input.cpp From 71f67df82b8b634de4331f4e1d9b1576bce9fcd7 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 28 Nov 2024 18:13:18 +0100 Subject: [PATCH 03/49] migrate test id for wrong type Signed-off-by: Martijn Govers --- .../native_api_tests/test_api_model_misc.cpp | 112 +++++++++++------- 1 file changed, 72 insertions(+), 40 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index 332610b9b..c089f3f12 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -371,9 +371,7 @@ struct State { auto default_model(State const& state) -> Model { return Model{50.0, state.get_input_dataset()}; } } // namespace -TEST_CASE("API Model - misc") { - State state; - +TEST_CASE("API Model - indexing + bad input") { SUBCASE("Test get indexer") { std::vector const node_id{1, 2, 3}; std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; @@ -410,8 +408,6 @@ TEST_CASE("API Model - misc") { std::vector link_id{5}; std::vector link_from_node{99}; std::vector link_to_node{3}; - std::vector link_from_status{1}; - std::vector link_to_status{1}; DatasetConst input_dataset{"input", 0, 1}; @@ -423,46 +419,82 @@ TEST_CASE("API Model - misc") { input_dataset.add_attribute_buffer("link", "id", link_id.data()); input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); - input_dataset.add_attribute_buffer("link", "from_status", link_from_status.data()); - input_dataset.add_attribute_buffer("link", "to_status", link_to_status.data()); auto construct_model = [&] { Model{50.0, input_dataset}; }; CHECK_THROWS_WITH_AS(construct_model(), "The id cannot be found: 99\n", PowerGridRegularError); } - // SUBCASE("Test id for wrong type") { // TODO(mgovers): needed; captured in Python test but not all flavors; maybe - // // move to test_main_core_input.cpp - // MainModel model2{50.0, meta_data::meta_data_gen::meta_data}; - - // state.link_input[0].from_node = 4; - // model2.add_component(state.node_input); // 1 2 3 - // model2.add_component(state.line_input); // 4 - // CHECK_THROWS_AS(model2.add_component(state.link_input), IDWrongType); - - // // Fix link input, retry - // state.link_input[0].from_node = 2; - // model2.add_component(state.link_input); // 5 - - // model2.add_component(state.source_input); // 6 10 - // model2.add_component(state.sym_load_input); // 7 - // model2.add_component(state.asym_load_input); // 8 - // model2.add_component(state.shunt_input); // 9 - - // // voltage sensor with a measured id which is not a node (link) - // state.sym_voltage_sensor_sensor_input[0].measured_object = 5; - // CHECK_THROWS_AS(model2.add_component(state.sym_voltage_sensor_sensor_input), IDWrongType); - - // // Test for all MeasuredTerminalType instances - // using enum MeasuredTerminalType; - // std::vector const mt_types{branch_from, branch_to, generator, load, shunt, source}; - - // // power sensor with terminal branch, with a measured id which is not a branch (node) - // for (auto const& mt_type : mt_types) { - // state.sym_power_sensor_input[0].measured_object = 1; - // state.sym_power_sensor_input[0].measured_terminal_type = mt_type; - // CHECK_THROWS_AS(model2.add_component(state.sym_power_sensor_input), IDWrongType); - // } - // } + SUBCASE("Test id for wrong type") { + std::vector const node_id{1, 2, 3}; + std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; + + std::vector line_id{9}; + std::vector line_from_node{1}; + std::vector line_to_node{2}; + + std::vector link_id{5}; + std::vector link_from_node{2}; + std::vector link_to_node{3}; + + std::vector sym_voltage_sensor_id{25}; + std::vector sym_voltage_sensor_measured_object{1}; + + std::vector sym_power_sensor_id{28}; + std::vector sym_power_sensor_measured_object{3}; + std::vector sym_power_sensor_measured_terminal_type{MeasuredTerminalType::node}; + + DatasetConst input_dataset{"input", 0, 1}; + + input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("node", "id", node_id.data()); + input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); + + input_dataset.add_buffer("line", line_id.size(), line_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("line", "id", line_id.data()); + input_dataset.add_attribute_buffer("line", "from_node", line_from_node.data()); + input_dataset.add_attribute_buffer("line", "to_node", line_to_node.data()); + + input_dataset.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("link", "id", link_id.data()); + input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); + input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); + + input_dataset.add_buffer("sym_voltage_sensor", sym_voltage_sensor_id.size(), sym_voltage_sensor_id.size(), + nullptr, nullptr); + input_dataset.add_attribute_buffer("sym_voltage_sensor", "id", sym_voltage_sensor_id.data()); + input_dataset.add_attribute_buffer("sym_voltage_sensor", "measured_object", + sym_voltage_sensor_measured_object.data()); + + input_dataset.add_buffer("sym_power_sensor", sym_power_sensor_id.size(), sym_power_sensor_id.size(), nullptr, + nullptr); + input_dataset.add_attribute_buffer("sym_power_sensor", "id", sym_power_sensor_id.data()); + input_dataset.add_attribute_buffer("sym_power_sensor", "measured_object", + sym_power_sensor_measured_object.data()); + input_dataset.add_attribute_buffer("sym_power_sensor", "measured_terminal_type", + sym_power_sensor_measured_terminal_type.data()); + + auto construct_model = [&] { Model{50.0, input_dataset}; }; + + SUBCASE("Correct type") { CHECK_NOTHROW(construct_model()); } + SUBCASE("Wrong branch terminal node") { + link_from_node[0] = 9; + CHECK_THROWS_WITH_AS(construct_model(), "Wrong type for object with id 9\n", PowerGridRegularError); + } + SUBCASE("Wrong voltage sensor measured object") { + sym_voltage_sensor_measured_object[0] = 5; + CHECK_THROWS_WITH_AS(construct_model(), "Wrong type for object with id 5\n", PowerGridRegularError); + } + SUBCASE("Wrong power sensor measured object type") { + using enum MeasuredTerminalType; + std::vector const mt_types{branch_from, branch_to, generator, load, shunt, source}; + + for (auto const& mt_type : mt_types) { + CAPTURE(mt_type); + sym_power_sensor_measured_terminal_type[0] = mt_type; + CHECK_THROWS_WITH_AS(construct_model(), "Wrong type for object with id 3\n", PowerGridRegularError); + } + } + } } // TEST_CASE_TEMPLATE("Test main model - unknown id", settings, regular_update, From e940d99ff4592f1c13ab1f7bddc8a859d8e698c8 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 28 Nov 2024 18:20:15 +0100 Subject: [PATCH 04/49] migrate update with unknown id Signed-off-by: Martijn Govers --- tests/native_api_tests/test_api_model_misc.cpp | 11 ----------- tests/native_api_tests/test_api_model_update.cpp | 3 ++- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index c089f3f12..4e1ab2e42 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -497,17 +497,6 @@ TEST_CASE("API Model - indexing + bad input") { } } -// TEST_CASE_TEMPLATE("Test main model - unknown id", settings, regular_update, -// cached_update) { // TODO(mgovers): we need this test -// State const state; -// auto model = default_model(state); - -// std::vector const source_update2{SourceUpdate{100, true, nan, nan}}; -// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; -// update_data.add_buffer("source", source_update2.size(), source_update2.size(), nullptr, source_update2.data()); -// CHECK_THROWS_AS((model.update_components(update_data)), IDNotFound); -// } - // TEST_CASE_TEMPLATE( // "Test main model - update only load", settings, regular_update, // cached_update) { // TODO(mgovers): we should whitebox-test this instead; values not reproduced by validation diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 8640a9ade..7abc79ba6 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -230,7 +230,8 @@ TEST_CASE_TEMPLATE( SUBCASE("Permanent update") { if constexpr (std::is_same_v) { - CHECK_THROWS_AS(model.update(update_dataset), PowerGridError); + CHECK_THROWS_WITH_AS(model.update(update_dataset), doctest::Contains("The id cannot be found"), + PowerGridError); } else { CHECK_NOTHROW(model.update(update_dataset)); } From eab6a6f24d223b6b01faca4842bc49263347c455 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Fri, 29 Nov 2024 08:45:38 +0100 Subject: [PATCH 05/49] remove some restore logic testing Signed-off-by: Martijn Govers --- .../native_api_tests/test_api_model_misc.cpp | 72 ------------------- 1 file changed, 72 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index 4e1ab2e42..079b84c8a 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -704,71 +704,6 @@ TEST_CASE("API Model - indexing + bad input") { // } // } -// TEST_CASE_TEMPLATE("Test main model - restore components", settings, regular_update, -// cached_update) { // TODO(mgovers): either whitebox (as a sub-part of batch impl) or otherwise drop -// // entirely (tested by batch update) -// State state; -// auto model = default_model(state); - -// auto const solver_output_orig = -// model.calculate(get_default_options(symmetric, CalculationMethod::linear)); - -// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; -// update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, -// state.sym_load_update.data()); -// update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, -// state.asym_load_update.data()); - -// model.update_components(update_data); -// model.restore_components(update_data); - -// SUBCASE("Symmetrical") { -// auto const solver_output_result = -// model.calculate(get_default_options(symmetric, -// CalculationMethod::linear)); -// model.output_result(solver_output_result, state.sym_node); -// model.output_result(solver_output_result, state.sym_branch); -// model.output_result(solver_output_result, state.sym_appliance); - -// CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); -// CHECK(state.sym_node[1].u_pu == doctest::Approx(test::u1)); -// CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); -// CHECK(state.sym_branch[0].i_from == doctest::Approx(test::i)); -// CHECK(state.sym_appliance[0].i == doctest::Approx(test::i)); -// CHECK(state.sym_appliance[1].i == doctest::Approx(0.0)); -// if constexpr (settings::update_type::value) { -// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load)); -// CHECK(state.sym_appliance[3].i == doctest::Approx(test::i_load)); -// } else { -// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load * 2)); -// CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); -// } -// CHECK(state.sym_appliance[4].i == doctest::Approx(test::i_shunt)); -// } -// SUBCASE("Asymmetrical") { -// auto const solver_output = model.calculate( -// get_default_options(asymmetric, CalculationMethod::linear)); -// model.output_result(solver_output, state.asym_node); -// model.output_result(solver_output, state.asym_branch); -// model.output_result(solver_output, state.asym_appliance); - -// CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); -// CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); -// CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); -// CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(test::i)); -// CHECK(state.asym_appliance[0].i(1) == doctest::Approx(test::i)); -// CHECK(state.asym_appliance[1].i(2) == doctest::Approx(0.0)); -// if constexpr (settings::update_type::value) { -// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load)); -// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(test::i_load)); -// } else { -// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2)); -// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); -// } -// CHECK(state.asym_appliance[4].i(2) == doctest::Approx(test::i_shunt)); -// } -// } - // TEST_CASE_TEMPLATE("Test main model - updates w/ alternating compute mode", settings, regular_update, // cached_update) { // TODO(mgovers): move to api tests; not possible with current validation // framework @@ -837,11 +772,6 @@ TEST_CASE("API Model - indexing + bad input") { // model.update_components(update_data); // } // } -// SUBCASE("With parameter change") { -// // Restore to original state and re-apply same update: causes param change for cached update -// model.restore_components(update_data); -// model.update_components(update_data); -// } // auto const math_output_asym_2 = // model.calculate(get_default_options(asymmetric, CalculationMethod::linear)); @@ -850,8 +780,6 @@ TEST_CASE("API Model - indexing + bad input") { // auto const math_output_sym_2 = // model.calculate(get_default_options(symmetric, CalculationMethod::linear)); // check_sym(model, math_output_sym_2); - -// model.restore_components(update_data); // } // namespace { From 6762c758cf440102a08424605241d6212566767d Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 10:51:31 +0100 Subject: [PATCH 06/49] attempt with heap corruption Signed-off-by: Martijn Govers --- docs/advanced_documentation/c-api.md | 2 +- .../include/power_grid_model_cpp/buffer.hpp | 1 + .../native_api_tests/test_api_model_misc.cpp | 255 ++++++++---------- 3 files changed, 111 insertions(+), 147 deletions(-) diff --git a/docs/advanced_documentation/c-api.md b/docs/advanced_documentation/c-api.md index 986b8eb8a..f8f8a17d2 100644 --- a/docs/advanced_documentation/c-api.md +++ b/docs/advanced_documentation/c-api.md @@ -116,7 +116,7 @@ and may vary between power grid model versions, compilers, operating systems and <-- 3 lines. | | | | | | | <-- alignment: a line may start every 4 bytes. iiiift iiiift iiiift <-- data: 6 bytes per line: 4 bytes for the ID, 1 for the from_status and 1 for the to_status. -| ..| ..| ..| <-- padding: (6 mod 4 = 2) bytes after every line. +| ..| ..| ..| <-- padding: (4 - (6 mod 4) = 2) bytes after every line. | | | | <-- aligned size: (6 + 2 = 8) bytes every line. ``` diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/buffer.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/buffer.hpp index 47bcc8156..d326eac00 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/buffer.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/buffer.hpp @@ -20,6 +20,7 @@ class Buffer { RawDataConstPtr get() const { return buffer_.get(); } RawDataPtr get() { return buffer_.get(); } + MetaComponent const* component() const { return component_; } Idx size() const { return size_; } void set_nan() { set_nan(0, size_); } diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index 079b84c8a..88935ecf1 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -2,12 +2,14 @@ // // SPDX-License-Identifier: MPL-2.0 +#include #include #include #include #include +#include #include namespace power_grid_model_cpp { @@ -40,7 +42,7 @@ enum class MeasuredTerminalType : IntS { }; enum class FaultType : IntS { - // three_phase = 0, + three_phase = 0, single_phase_to_ground = 1, // two_phase = 2, // two_phase_to_ground = 3, @@ -48,7 +50,7 @@ enum class FaultType : IntS { }; enum class FaultPhase : IntS { - // abc = 0, + abc = 0, a = 1, // b = 2, // c = 3, @@ -497,159 +499,120 @@ TEST_CASE("API Model - indexing + bad input") { } } -// TEST_CASE_TEMPLATE( -// "Test main model - update only load", settings, regular_update, -// cached_update) { // TODO(mgovers): we should whitebox-test this instead; values not reproduced by validation -// tests State state; auto model = default_model(state); +TEST_CASE("API model - all updates") { + using namespace std::string_literals; -// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; -// update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, -// state.sym_load_update.data()); -// update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, -// state.asym_load_update.data()); -// model.update_components(update_data); + State state; + auto const input_dataset = state.get_input_dataset(); + auto const& input_info = input_dataset.get_info(); + auto model = Model{50.0, input_dataset}; -// SUBCASE("Symmetrical") { -// auto const solver_output = -// model.calculate(get_default_options(symmetric, -// CalculationMethod::linear)); -// model.output_result(solver_output, state.sym_node); -// model.output_result(solver_output, state.sym_branch); -// model.output_result(solver_output, state.sym_appliance); -// CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); -// CHECK(state.sym_node[1].u_pu == doctest::Approx(test::u1)); -// CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); -// CHECK(state.sym_branch[0].i_from == doctest::Approx(test::i)); -// CHECK(state.sym_appliance[0].i == doctest::Approx(test::i)); -// CHECK(state.sym_appliance[1].i == doctest::Approx(0.0)); -// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load * 2)); -// CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); -// CHECK(state.sym_appliance[4].i == doctest::Approx(test::i_shunt)); -// } -// SUBCASE("Asymmetrical") { -// auto const solver_output = model.calculate( -// get_default_options(asymmetric, CalculationMethod::linear)); -// model.output_result(solver_output, state.asym_node); -// model.output_result(solver_output, state.asym_branch); -// model.output_result(solver_output, state.asym_appliance); -// CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); -// CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); -// CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); -// CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(test::i)); -// CHECK(state.asym_appliance[0].i(1) == doctest::Approx(test::i)); -// CHECK(state.asym_appliance[1].i(2) == doctest::Approx(0.0)); -// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2)); -// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); -// CHECK(state.asym_appliance[4].i(2) == doctest::Approx(test::i_shunt)); -// } -// } + // // update vector + // std::vector sym_load_update_id{7}; + // std::vector sym_load_update_status{1}; + // std::vector sym_load_update_p_specified{2.5e6}; + + // std::vector asym_load_update_id{8}; + // std::vector asym_load_update_status{0}; + + // std::vector shunt_update_id{9}; + // std::vector shunt_update_status{0}; + // std::vector shunt_update_b1{0.02}; + // std::vector shunt_update_b0{0.02}; + + // // used for test case alternate compute mode + // std::vector shunt_update_2_id{6}; + // std::vector source_update_2_status{0}; + // std::vector shunt_update_2_b1{0.01}; + // std::vector shunt_update_2_b0{0.01}; + + // std::vector source_update_id{10}; + // std::vector source_update_status{1}; + // std::vector source_update_u_ref{test::u1}; + + // std::vector link_update_id{5}; + // std::vector link_update_from_status{1}; + // std::vector link_update_to_status{0}; + + // std::vector fault_update_id{30}; + // std::vector fault_update_status{1}; + // std::vector fault_update_type{FaultType::three_phase}; + // std::vector fault_update_phase{FaultPhase::abc}; + // std::vector fault_update_object{1}; + + // DatasetConst update_data{"update", 1, 1}; + // update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); + // update_data.add_attribute_buffer("sym_load", "id", sym_load_update_id.data()); + // update_data.add_attribute_buffer("sym_load", "status", sym_load_update_status.data()); + // update_data.add_attribute_buffer("sym_load", "p_specified", sym_load_update_p_specified.data()); + + // update_data.add_buffer("asym_load", 1, 1, nullptr, nullptr); + // update_data.add_attribute_buffer("asym_load", "id", asym_load_update_id.data()); + // update_data.add_attribute_buffer("asym_load", "status", asym_load_update_status.data()); + + // update_data.add_buffer("shunt", 1, 1, nullptr, nullptr); + // update_data.add_attribute_buffer("shunt", "id", shunt_update_id.data()); + // update_data.add_attribute_buffer("shunt", "status", shunt_update_status.data()); + // update_data.add_attribute_buffer("shunt", "b1", shunt_update_b1.data()); + // update_data.add_attribute_buffer("shunt", "b0", shunt_update_b0.data()); + + // update_data.add_buffer("source", 1, 1, nullptr, nullptr); + // update_data.add_attribute_buffer("source", "id", source_update_id.data()); + // update_data.add_attribute_buffer("source", "status", source_update_status.data()); + // update_data.add_attribute_buffer("source", "u_ref", source_update_u_ref.data()); + + // update_data.add_buffer("link", 1, 1, nullptr, nullptr); + // update_data.add_attribute_buffer("link", "id", link_update_id.data()); + // update_data.add_attribute_buffer("link", "from_status", link_update_from_status.data()); + // update_data.add_attribute_buffer("link", "to_status", link_update_to_status.data()); + + // update_data.add_buffer("fault", 1, 1, nullptr, nullptr); + // update_data.add_attribute_buffer("fault", "id", fault_update_id.data()); + // update_data.add_attribute_buffer("fault", "status", fault_update_status.data()); + // update_data.add_attribute_buffer("fault", "fault_type", fault_update_type.data()); + // update_data.add_attribute_buffer("fault", "fault_phase", fault_update_phase.data()); + // update_data.add_attribute_buffer("fault", "fault_object", fault_update_object.data()); + + auto const output_dataset_type = "sym_output"s; + for (Idx comp_type_idx = 0; comp_type_idx < input_info.n_components(); ++comp_type_idx) { + CAPTURE(comp_type_idx); + + auto const comp_type = input_info.component_name(comp_type_idx); + CAPTURE(comp_type); + + if (comp_type != "node") { // TODO(mgovers): remove + continue; + } -// TEST_CASE_TEMPLATE( -// "Test main model - update load and shunt param", settings, regular_update, -// cached_update) { // TODO(mgovers): we should whitebox-test this instead; values not reproduced by validation -// tests State state; auto model = default_model(state); + auto const comp_meta = MetaData::get_component_by_name(output_dataset_type, comp_type); -// state.sym_load_update[0].p_specified = 2.5e6; -// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; -// update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, -// state.sym_load_update.data()); -// update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, -// state.asym_load_update.data()); -// update_data.add_buffer("shunt", state.shunt_update.size(), state.shunt_update.size(), nullptr, -// state.shunt_update.data()); -// model.update_components(update_data); + // DatasetMutable output_data_from_batch{output_dataset_type, 1, 1}; + DatasetMutable output_data_from_updated_single{output_dataset_type, false, 1}; -// SUBCASE("Symmetrical") { -// auto const solver_output = -// model.calculate(get_default_options(symmetric, -// CalculationMethod::linear)); -// model.output_result(solver_output, state.sym_node); -// model.output_result(solver_output, state.sym_branch); -// model.output_result(solver_output, state.sym_appliance); -// CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); -// CHECK(state.sym_node[1].u_pu == doctest::Approx(test::u1)); -// CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); -// CHECK(state.sym_branch[0].i_from == doctest::Approx(test::i)); -// CHECK(state.sym_appliance[0].i == doctest::Approx(test::i)); -// CHECK(state.sym_appliance[1].i == doctest::Approx(0.0)); -// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load * 2 + test::i_shunt)); -// CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); -// CHECK(state.sym_appliance[4].i == doctest::Approx(0.0)); -// } -// SUBCASE("Asymmetrical") { -// auto const solver_output = model.calculate( -// get_default_options(asymmetric, CalculationMethod::linear)); -// model.output_result(solver_output, state.asym_node); -// model.output_result(solver_output, state.asym_branch); -// model.output_result(solver_output, state.asym_appliance); -// CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); -// CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); -// CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); -// CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(test::i)); -// CHECK(state.asym_appliance[0].i(1) == doctest::Approx(test::i)); -// CHECK(state.asym_appliance[1].i(2) == doctest::Approx(0.0)); -// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2 + test::i_shunt)); -// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); -// CHECK(state.asym_appliance[4].i(2) == doctest::Approx(0.0)); -// } -// } + auto const total_elements = input_info.component_total_elements(comp_type_idx); + auto const elements_per_scenario = input_info.component_elements_per_scenario(comp_type_idx); + auto const n_bytes = total_elements * MetaData::component_size(comp_meta); -// TEST_CASE_TEMPLATE( -// "Test main model - all updates", settings, regular_update, -// cached_update) { // TODO(mgovers): we should whitebox-test this instead; values not reproduced by validation -// tests State state; auto model = default_model(state); + // Buffer sym_output_from_batch{comp_meta, total_elements}; + Buffer sym_output_from_updated_single{comp_meta, total_elements}; + // std::vector sym_output_from_updated_single_data(n_bytes); -// state.sym_load_update[0].p_specified = 2.5e6; -// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; -// update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, -// state.sym_load_update.data()); -// update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, -// state.asym_load_update.data()); -// update_data.add_buffer("shunt", state.shunt_update.size(), state.shunt_update.size(), nullptr, -// state.shunt_update.data()); -// update_data.add_buffer("source", state.source_update.size(), state.source_update.size(), nullptr, -// state.source_update.data()); -// update_data.add_buffer("link", state.link_update.size(), state.link_update.size(), nullptr, -// state.link_update.data()); -// update_data.add_buffer("fault", state.fault_update.size(), state.fault_update.size(), nullptr, -// state.fault_update.data()); + // output_data_from_batch.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, + // sym_output_from_batch); + output_data_from_updated_single.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, + sym_output_from_updated_single); + // output_data_from_updated_single.add_buffer( + // comp_type, elements_per_scenario, total_elements, nullptr, + // reinterpret_cast(sym_output_from_updated_single_data.data())); -// model.update_components(update_data); + auto opt = get_default_options(CalculationSymmetry::symmetric, PGM_linear); + // model.calculate(opt, output_data_from_batch, update_data); + // model.update(update_data); + model.calculate(opt, output_data_from_updated_single); -// SUBCASE("Symmetrical") { -// auto const solver_output = -// model.calculate(get_default_options(symmetric, -// CalculationMethod::linear)); -// model.output_result(solver_output, state.sym_node); -// model.output_result(solver_output, state.sym_branch); -// model.output_result(solver_output, state.sym_appliance); -// CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); -// CHECK(state.sym_node[1].u_pu == doctest::Approx(1.05)); -// CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); -// CHECK(state.sym_branch[0].i_from == doctest::Approx(0.0).epsilon(1e-6)); -// CHECK(state.sym_appliance[0].i == doctest::Approx(0.0).epsilon(1e-6)); -// CHECK(state.sym_appliance[1].i == doctest::Approx(test::i)); -// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i)); -// CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); -// CHECK(state.sym_appliance[4].i == doctest::Approx(0.0)); -// } -// SUBCASE("Asymmetrical") { -// auto const solver_output = model.calculate( -// get_default_options(asymmetric, CalculationMethod::linear)); -// model.output_result(solver_output, state.asym_node); -// model.output_result(solver_output, state.asym_branch); -// model.output_result(solver_output, state.asym_appliance); -// CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); -// CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(1.05)); -// CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); -// CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(0.0).epsilon(1e-6)); -// CHECK(state.asym_appliance[0].i(1) == doctest::Approx(0.0).epsilon(1e-6)); -// CHECK(state.asym_appliance[1].i(2) == doctest::Approx(test::i)); -// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i)); -// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); -// CHECK(state.asym_appliance[4].i(2) == doctest::Approx(0.0)); -// } -// } + // CHECK(sym_output_from_batch == sym_output_from_updated_single); + } +} // TEST_CASE_TEMPLATE("Test main model - single permanent update from batch", settings, regular_update, // cached_update) { // TODO(mgovers): we should whitebox-test this instead From 88114eddc08f9fb508263aebb21a3b9b12c6bbf4 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 11:22:39 +0100 Subject: [PATCH 07/49] fix heap corruption Signed-off-by: Martijn Govers --- tests/native_api_tests/test_api_model_misc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index 88935ecf1..e2d661f89 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -22,7 +22,7 @@ constexpr double nan = std::numeric_limits::quiet_NaN(); constexpr double deg_120 = 2.0 / 3.0 * pi; constexpr double deg_240 = 4.0 / 3.0 * pi; -enum class CalculationSymmetry : Idx { symmetric = 0, asymmetric = 1 }; +enum class CalculationSymmetry : Idx { symmetric = 1, asymmetric = 0 }; enum class LoadGenType : IntS { const_pq = 0, // constant power const_y = 1, // constant element_admittance (impedance) @@ -370,7 +370,7 @@ struct State { // {30, 1, FaultType::three_phase, FaultPhase::abc, 1, nan, nan}, {30}, {30}, {30}, {30}}; }; -auto default_model(State const& state) -> Model { return Model{50.0, state.get_input_dataset()}; } +// auto default_model(State const& state) -> Model { return Model{50.0, state.get_input_dataset()}; } } // namespace TEST_CASE("API Model - indexing + bad input") { @@ -591,7 +591,7 @@ TEST_CASE("API model - all updates") { auto const total_elements = input_info.component_total_elements(comp_type_idx); auto const elements_per_scenario = input_info.component_elements_per_scenario(comp_type_idx); - auto const n_bytes = total_elements * MetaData::component_size(comp_meta); + // auto const n_bytes = total_elements * MetaData::component_size(comp_meta); // Buffer sym_output_from_batch{comp_meta, total_elements}; Buffer sym_output_from_updated_single{comp_meta, total_elements}; From d237feee57311789ab66ed7559fff165020b7bb8 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 13:16:02 +0100 Subject: [PATCH 08/49] add enum for calculation symmetry Signed-off-by: Martijn Govers --- .../include/power_grid_model/common/enum.hpp | 2 +- .../include/power_grid_model_c/basics.h | 8 ++++++++ .../include/power_grid_model_c/options.h | 2 +- power_grid_model_c/power_grid_model_c/src/model.cpp | 8 ++++++-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp index e24291164..1cf10ab2d 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp @@ -24,7 +24,7 @@ enum class ControlSide : IntS { from = 0, to = 1, side_1 = 0, side_2 = 1, side_3 enum class CalculationType : IntS { power_flow = 0, state_estimation = 1, short_circuit = 2 }; -enum class CalculationSymmetry : IntS { symmetric = 0, asymmetric = 1 }; +enum class CalculationSymmetry : IntS { symmetric = 1, asymmetric = 2 }; enum class CalculationMethod : IntS { default_method = -128, diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h index ff39e9750..6b769c596 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h @@ -166,6 +166,14 @@ enum PGM_CalculationMethod { PGM_iec60909 = 5 /**< fault analysis for short circuits using the iec60909 standard */ }; +/** + * @brief Enumeration for calculation and/or component symmetry + */ +enum PGM_SymmetryType { + PGM_asymmetric = 0, /** < asymmetric calculation and/or component */ + PGM_symmetric = 1 /** < symmetric calculation and/or component */ +}; + /** * @brief Enumeration of error codes. * diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h index e1db736ce..3671727a0 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h @@ -66,7 +66,7 @@ PGM_API void PGM_set_calculation_method(PGM_Handle* handle, PGM_Options* opt, PG * * @param handle * @param opt The pointer to the option instance. - * @param sym 1 for symmetric calculation; 0 for asymmetric calculation. + * @param sym See #PGM_CalculationSymmetry . 1 for symmetric calculation; 0 for asymmetric calculation. */ PGM_API void PGM_set_symmetric(PGM_Handle* handle, PGM_Options* opt, PGM_Idx sym); diff --git a/power_grid_model_c/power_grid_model_c/src/model.cpp b/power_grid_model_c/power_grid_model_c/src/model.cpp index fa4f5c521..8c921c6f9 100644 --- a/power_grid_model_c/power_grid_model_c/src/model.cpp +++ b/power_grid_model_c/power_grid_model_c/src/model.cpp @@ -75,10 +75,14 @@ constexpr auto get_calculation_type(PGM_Options const& opt) { } constexpr auto get_calculation_symmetry(PGM_Options const& opt) { - if (opt.symmetric == 0) { + switch (opt.symmetric) { + case PGM_asymmetric: return CalculationSymmetry::asymmetric; + case PGM_symmetric: + return CalculationSymmetry::symmetric; + default: + throw MissingCaseForEnumError{"get_calculation_symmetry", opt.tap_changing_strategy}; } - return CalculationSymmetry::symmetric; } constexpr auto get_calculation_method(PGM_Options const& opt) { From 15e4a89263e64d06d7ccb4ffa4d9571252326062 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 13:22:55 +0100 Subject: [PATCH 09/49] minor Signed-off-by: Martijn Govers --- .../cpp_validation_tests/test_validation.cpp | 2 +- .../native_api_tests/test_api_model_misc.cpp | 169 +++++++++--------- 2 files changed, 82 insertions(+), 89 deletions(-) diff --git a/tests/cpp_validation_tests/test_validation.cpp b/tests/cpp_validation_tests/test_validation.cpp index eeb3c7a03..24cc40c5a 100644 --- a/tests/cpp_validation_tests/test_validation.cpp +++ b/tests/cpp_validation_tests/test_validation.cpp @@ -384,7 +384,7 @@ Options get_options(CaseParam const& param, Idx threading = -1) { Options options{}; options.set_calculation_type(calculation_type_mapping.at(param.calculation_type)); options.set_calculation_method(calculation_method_mapping.at(param.calculation_method)); - options.set_symmetric(param.sym ? 1 : 0); + options.set_symmetric(param.sym ? PGM_symmetric : PGM_asymmetric); options.set_err_tol(param.err_tol); options.set_max_iter(param.max_iter); options.set_threading(threading); diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index e2d661f89..a42aa02a0 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: MPL-2.0 +#include #include #include @@ -22,7 +23,7 @@ constexpr double nan = std::numeric_limits::quiet_NaN(); constexpr double deg_120 = 2.0 / 3.0 * pi; constexpr double deg_240 = 4.0 / 3.0 * pi; -enum class CalculationSymmetry : Idx { symmetric = 1, asymmetric = 0 }; +enum class CalculationSymmetry : Idx { symmetric = PGM_symmetric, asymmetric = PGM_asymmetric }; enum class LoadGenType : IntS { const_pq = 0, // constant power const_y = 1, // constant element_admittance (impedance) @@ -61,11 +62,11 @@ enum class FaultPhase : IntS { // nan = na_IntS }; -Options get_default_options(CalculationSymmetry calculation_symmetry, PGM_CalculationMethod calculation_method, +Options get_default_options(PGM_SymmetryType calculation_symmetry, PGM_CalculationMethod calculation_method, Idx threading = default_option) { Options opt; opt.set_calculation_type(PGM_power_flow); - opt.set_symmetric(static_cast(calculation_symmetry)); + opt.set_symmetric(calculation_symmetry); opt.set_calculation_method(calculation_method); if (threading != default_option) { opt.set_threading(threading); @@ -507,71 +508,71 @@ TEST_CASE("API model - all updates") { auto const& input_info = input_dataset.get_info(); auto model = Model{50.0, input_dataset}; - // // update vector - // std::vector sym_load_update_id{7}; - // std::vector sym_load_update_status{1}; - // std::vector sym_load_update_p_specified{2.5e6}; - - // std::vector asym_load_update_id{8}; - // std::vector asym_load_update_status{0}; - - // std::vector shunt_update_id{9}; - // std::vector shunt_update_status{0}; - // std::vector shunt_update_b1{0.02}; - // std::vector shunt_update_b0{0.02}; - - // // used for test case alternate compute mode - // std::vector shunt_update_2_id{6}; - // std::vector source_update_2_status{0}; - // std::vector shunt_update_2_b1{0.01}; - // std::vector shunt_update_2_b0{0.01}; - - // std::vector source_update_id{10}; - // std::vector source_update_status{1}; - // std::vector source_update_u_ref{test::u1}; - - // std::vector link_update_id{5}; - // std::vector link_update_from_status{1}; - // std::vector link_update_to_status{0}; - - // std::vector fault_update_id{30}; - // std::vector fault_update_status{1}; - // std::vector fault_update_type{FaultType::three_phase}; - // std::vector fault_update_phase{FaultPhase::abc}; - // std::vector fault_update_object{1}; - - // DatasetConst update_data{"update", 1, 1}; - // update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); - // update_data.add_attribute_buffer("sym_load", "id", sym_load_update_id.data()); - // update_data.add_attribute_buffer("sym_load", "status", sym_load_update_status.data()); - // update_data.add_attribute_buffer("sym_load", "p_specified", sym_load_update_p_specified.data()); - - // update_data.add_buffer("asym_load", 1, 1, nullptr, nullptr); - // update_data.add_attribute_buffer("asym_load", "id", asym_load_update_id.data()); - // update_data.add_attribute_buffer("asym_load", "status", asym_load_update_status.data()); - - // update_data.add_buffer("shunt", 1, 1, nullptr, nullptr); - // update_data.add_attribute_buffer("shunt", "id", shunt_update_id.data()); - // update_data.add_attribute_buffer("shunt", "status", shunt_update_status.data()); - // update_data.add_attribute_buffer("shunt", "b1", shunt_update_b1.data()); - // update_data.add_attribute_buffer("shunt", "b0", shunt_update_b0.data()); - - // update_data.add_buffer("source", 1, 1, nullptr, nullptr); - // update_data.add_attribute_buffer("source", "id", source_update_id.data()); - // update_data.add_attribute_buffer("source", "status", source_update_status.data()); - // update_data.add_attribute_buffer("source", "u_ref", source_update_u_ref.data()); - - // update_data.add_buffer("link", 1, 1, nullptr, nullptr); - // update_data.add_attribute_buffer("link", "id", link_update_id.data()); - // update_data.add_attribute_buffer("link", "from_status", link_update_from_status.data()); - // update_data.add_attribute_buffer("link", "to_status", link_update_to_status.data()); - - // update_data.add_buffer("fault", 1, 1, nullptr, nullptr); - // update_data.add_attribute_buffer("fault", "id", fault_update_id.data()); - // update_data.add_attribute_buffer("fault", "status", fault_update_status.data()); - // update_data.add_attribute_buffer("fault", "fault_type", fault_update_type.data()); - // update_data.add_attribute_buffer("fault", "fault_phase", fault_update_phase.data()); - // update_data.add_attribute_buffer("fault", "fault_object", fault_update_object.data()); + // update vector + std::vector sym_load_update_id{7}; + std::vector sym_load_update_status{1}; + std::vector sym_load_update_p_specified{2.5e6}; + + std::vector asym_load_update_id{8}; + std::vector asym_load_update_status{0}; + + std::vector shunt_update_id{9}; + std::vector shunt_update_status{0}; + std::vector shunt_update_b1{0.02}; + std::vector shunt_update_b0{0.02}; + + // used for test case alternate compute mode + std::vector shunt_update_2_id{6}; + std::vector source_update_2_status{0}; + std::vector shunt_update_2_b1{0.01}; + std::vector shunt_update_2_b0{0.01}; + + std::vector source_update_id{10}; + std::vector source_update_status{1}; + std::vector source_update_u_ref{test::u1}; + + std::vector link_update_id{5}; + std::vector link_update_from_status{1}; + std::vector link_update_to_status{0}; + + std::vector fault_update_id{30}; + std::vector fault_update_status{1}; + std::vector fault_update_type{FaultType::three_phase}; + std::vector fault_update_phase{FaultPhase::abc}; + std::vector fault_update_object{1}; + + DatasetConst update_data{"update", 1, 1}; + update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("sym_load", "id", sym_load_update_id.data()); + update_data.add_attribute_buffer("sym_load", "status", sym_load_update_status.data()); + update_data.add_attribute_buffer("sym_load", "p_specified", sym_load_update_p_specified.data()); + + update_data.add_buffer("asym_load", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("asym_load", "id", asym_load_update_id.data()); + update_data.add_attribute_buffer("asym_load", "status", asym_load_update_status.data()); + + update_data.add_buffer("shunt", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("shunt", "id", shunt_update_id.data()); + update_data.add_attribute_buffer("shunt", "status", shunt_update_status.data()); + update_data.add_attribute_buffer("shunt", "b1", shunt_update_b1.data()); + update_data.add_attribute_buffer("shunt", "b0", shunt_update_b0.data()); + + update_data.add_buffer("source", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("source", "id", source_update_id.data()); + update_data.add_attribute_buffer("source", "status", source_update_status.data()); + update_data.add_attribute_buffer("source", "u_ref", source_update_u_ref.data()); + + update_data.add_buffer("link", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("link", "id", link_update_id.data()); + update_data.add_attribute_buffer("link", "from_status", link_update_from_status.data()); + update_data.add_attribute_buffer("link", "to_status", link_update_to_status.data()); + + update_data.add_buffer("fault", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("fault", "id", fault_update_id.data()); + update_data.add_attribute_buffer("fault", "status", fault_update_status.data()); + update_data.add_attribute_buffer("fault", "fault_type", fault_update_type.data()); + update_data.add_attribute_buffer("fault", "fault_phase", fault_update_phase.data()); + update_data.add_attribute_buffer("fault", "fault_object", fault_update_object.data()); auto const output_dataset_type = "sym_output"s; for (Idx comp_type_idx = 0; comp_type_idx < input_info.n_components(); ++comp_type_idx) { @@ -580,37 +581,29 @@ TEST_CASE("API model - all updates") { auto const comp_type = input_info.component_name(comp_type_idx); CAPTURE(comp_type); - if (comp_type != "node") { // TODO(mgovers): remove - continue; - } - auto const comp_meta = MetaData::get_component_by_name(output_dataset_type, comp_type); - // DatasetMutable output_data_from_batch{output_dataset_type, 1, 1}; + DatasetMutable output_data_from_batch{output_dataset_type, 1, 1}; DatasetMutable output_data_from_updated_single{output_dataset_type, false, 1}; auto const total_elements = input_info.component_total_elements(comp_type_idx); auto const elements_per_scenario = input_info.component_elements_per_scenario(comp_type_idx); - // auto const n_bytes = total_elements * MetaData::component_size(comp_meta); + auto const n_bytes = total_elements * MetaData::component_size(comp_meta); - // Buffer sym_output_from_batch{comp_meta, total_elements}; - Buffer sym_output_from_updated_single{comp_meta, total_elements}; - // std::vector sym_output_from_updated_single_data(n_bytes); + std::vector sym_output_from_batch(n_bytes); + std::vector sym_output_from_updated_single(n_bytes); - // output_data_from_batch.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, - // sym_output_from_batch); + output_data_from_batch.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, + reinterpret_cast(sym_output_from_batch.data())); output_data_from_updated_single.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, - sym_output_from_updated_single); - // output_data_from_updated_single.add_buffer( - // comp_type, elements_per_scenario, total_elements, nullptr, - // reinterpret_cast(sym_output_from_updated_single_data.data())); - - auto opt = get_default_options(CalculationSymmetry::symmetric, PGM_linear); - // model.calculate(opt, output_data_from_batch, update_data); - // model.update(update_data); + reinterpret_cast(sym_output_from_updated_single.data())); + + auto opt = get_default_options(PGM_symmetric, PGM_linear); + model.calculate(opt, output_data_from_batch, update_data); + model.update(update_data); model.calculate(opt, output_data_from_updated_single); - // CHECK(sym_output_from_batch == sym_output_from_updated_single); + CHECK(sym_output_from_batch == sym_output_from_updated_single); } } From cc254ef4f49e2f43ecc49abd407710e0b4c56ad7 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 13:30:56 +0100 Subject: [PATCH 10/49] move misc api model test to regular api model test Signed-off-by: Martijn Govers --- tests/native_api_tests/test_api_model.cpp | 137 +++++++++++++ .../native_api_tests/test_api_model_misc.cpp | 181 +----------------- 2 files changed, 138 insertions(+), 180 deletions(-) diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index ad5a4bf4f..64fb99dda 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -58,6 +58,19 @@ Dataset created with the following buffers: namespace power_grid_model_cpp { namespace { +enum class MeasuredTerminalType : IntS { + branch_from = 0, + branch_to = 1, + source = 2, + shunt = 3, + load = 4, + generator = 5, + branch3_1 = 6, + branch3_2 = 7, + branch3_3 = 8, + node = 9 +}; + void check_exception(PowerGridError const& e, PGM_ErrorCode const& reference_error, std::string_view reference_err_msg) { CHECK(e.error_code() == reference_error); @@ -829,6 +842,130 @@ TEST_CASE("API Model") { PowerGridRegularError); } } + + SUBCASE("Test get indexer") { + std::vector const node_id{1, 2, 3}; + std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; + + DatasetConst input_dataset{"input", 0, 1}; + input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("node", "id", node_id.data()); + input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); + + auto model = Model{50.0, input_dataset}; + + std::vector const ids_to_index{2, 1, 3, 2}; + std::vector const expected_indexer{1, 0, 2, 1}; + std::vector indexer(ids_to_index.size()); + model.get_indexer("node", ids_to_index.size(), ids_to_index.data(), indexer.data()); + CHECK(indexer == expected_indexer); + } + + SUBCASE("Test duplicated id") { + std::vector node_id{1, 1, 3}; + DatasetConst input_dataset{"input", 0, 1}; + + input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("node", "id", node_id.data()); + + auto construct_model = [&] { Model{50.0, input_dataset}; }; + CHECK_THROWS_WITH_AS(construct_model(), "Conflicting id detected: 1\n", PowerGridRegularError); + } + + SUBCASE("Test non-existing id") { + std::vector const node_id{1, 2, 3}; + std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; + + std::vector link_id{5}; + std::vector link_from_node{99}; + std::vector link_to_node{3}; + + DatasetConst input_dataset{"input", 0, 1}; + + input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("node", "id", node_id.data()); + input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); + + input_dataset.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("link", "id", link_id.data()); + input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); + input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); + + auto construct_model = [&] { Model{50.0, input_dataset}; }; + CHECK_THROWS_WITH_AS(construct_model(), "The id cannot be found: 99\n", PowerGridRegularError); + } + + SUBCASE("Test id for wrong type") { + std::vector const node_id{1, 2, 3}; + std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; + + std::vector line_id{9}; + std::vector line_from_node{1}; + std::vector line_to_node{2}; + + std::vector link_id{5}; + std::vector link_from_node{2}; + std::vector link_to_node{3}; + + std::vector sym_voltage_sensor_id{25}; + std::vector sym_voltage_sensor_measured_object{1}; + + std::vector sym_power_sensor_id{28}; + std::vector sym_power_sensor_measured_object{3}; + std::vector sym_power_sensor_measured_terminal_type{MeasuredTerminalType::node}; + + DatasetConst input_dataset{"input", 0, 1}; + + input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("node", "id", node_id.data()); + input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); + + input_dataset.add_buffer("line", line_id.size(), line_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("line", "id", line_id.data()); + input_dataset.add_attribute_buffer("line", "from_node", line_from_node.data()); + input_dataset.add_attribute_buffer("line", "to_node", line_to_node.data()); + + input_dataset.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); + input_dataset.add_attribute_buffer("link", "id", link_id.data()); + input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); + input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); + + input_dataset.add_buffer("sym_voltage_sensor", sym_voltage_sensor_id.size(), sym_voltage_sensor_id.size(), + nullptr, nullptr); + input_dataset.add_attribute_buffer("sym_voltage_sensor", "id", sym_voltage_sensor_id.data()); + input_dataset.add_attribute_buffer("sym_voltage_sensor", "measured_object", + sym_voltage_sensor_measured_object.data()); + + input_dataset.add_buffer("sym_power_sensor", sym_power_sensor_id.size(), sym_power_sensor_id.size(), nullptr, + nullptr); + input_dataset.add_attribute_buffer("sym_power_sensor", "id", sym_power_sensor_id.data()); + input_dataset.add_attribute_buffer("sym_power_sensor", "measured_object", + sym_power_sensor_measured_object.data()); + input_dataset.add_attribute_buffer("sym_power_sensor", "measured_terminal_type", + sym_power_sensor_measured_terminal_type.data()); + + auto construct_model = [&] { Model{50.0, input_dataset}; }; + + SUBCASE("Correct type") { CHECK_NOTHROW(construct_model()); } + SUBCASE("Wrong branch terminal node") { + link_from_node[0] = 9; + CHECK_THROWS_WITH_AS(construct_model(), "Wrong type for object with id 9\n", PowerGridRegularError); + } + SUBCASE("Wrong voltage sensor measured object") { + sym_voltage_sensor_measured_object[0] = 5; + CHECK_THROWS_WITH_AS(construct_model(), "Wrong type for object with id 5\n", PowerGridRegularError); + } + SUBCASE("Wrong power sensor measured object type") { + using enum MeasuredTerminalType; + std::vector const mt_types{branch_from, branch_to, generator, load, shunt, source}; + + for (auto const& mt_type : mt_types) { + CAPTURE(mt_type); + sym_power_sensor_measured_terminal_type[0] = mt_type; + CHECK_THROWS_WITH_AS(construct_model(), "Wrong type for object with id 3\n", PowerGridRegularError); + } + } + } } } // namespace power_grid_model_cpp diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index a42aa02a0..83253fdeb 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -42,26 +42,6 @@ enum class MeasuredTerminalType : IntS { node = 9 }; -enum class FaultType : IntS { - three_phase = 0, - single_phase_to_ground = 1, - // two_phase = 2, - // two_phase_to_ground = 3, - // nan = -128 -}; - -enum class FaultPhase : IntS { - abc = 0, - a = 1, - // b = 2, - // c = 3, - // ab = 4, - // ac = 5, - // bc = 6, - // default_value = -1, - // nan = na_IntS -}; - Options get_default_options(PGM_SymmetryType calculation_symmetry, PGM_CalculationMethod calculation_method, Idx threading = default_option) { Options opt; @@ -193,14 +173,6 @@ struct State { std::vector asym_voltage_sensor_u_measured{10.31e3 / sqrt3, 10.32e3 / sqrt3, 10.33e3 / sqrt3}; std::vector asym_voltage_sensor_u_angle_measured{0.0, -deg_120, -deg_240}; - std::vector fault_id{30}; - std::vector fault_status{1}; - std::vector fault_type{FaultType::single_phase_to_ground}; - std::vector fault_phase{FaultPhase::a}; - std::vector fault_object{3}; - std::vector fault_r_f{0.1}; - std::vector fault_x_f{0.1}; - auto get_input_dataset() const { DatasetConst result{"input", 0, 1}; @@ -302,15 +274,6 @@ struct State { result.add_attribute_buffer("asym_voltage_sensor", "u_angle_measured", asym_voltage_sensor_u_angle_measured.data()); - result.add_buffer("fault", fault_id.size(), fault_id.size(), nullptr, nullptr); - result.add_attribute_buffer("fault", "id", fault_id.data()); - result.add_attribute_buffer("fault", "status", fault_status.data()); - result.add_attribute_buffer("fault", "fault_type", fault_type.data()); - result.add_attribute_buffer("fault", "fault_phase", fault_phase.data()); - result.add_attribute_buffer("fault", "fault_object", fault_object.data()); - result.add_attribute_buffer("fault", "r_f", fault_r_f.data()); - result.add_attribute_buffer("fault", "x_f", fault_x_f.data()); - return result; } @@ -358,7 +321,7 @@ struct State { // RealValue{nan}}}; std::vector shunt_update{{9, 0, nan, 0.02, nan, 0.02}}; // std::vector shunt_update_2{{6, 0, nan, 0.01, nan, 0.01}}; // used for test case alternate compute // mode std::vector source_update{{10, 1, test::u1, nan}}; std::vector link_update{{5, - // 1, 0}}; std::vector fault_update{{30, 1, FaultType::three_phase, FaultPhase::abc, 1, nan, nan}}; + // 1, 0}}; // // batch update vector // std::vector batch_sym_load_update{{7, 1, 1.0e6, nan}, {7}, {7}, {7}, {7}}; @@ -367,139 +330,11 @@ struct State { // std::vector batch_shunt_update{{9, 0, nan, 0.02, nan, 0.02}, {9}, {9}, {9}, {9}}; // std::vector batch_source_update{{10, 1, test::u1, nan}, {10}, {10}, {10}, {10}}; // std::vector batch_link_update{{5, 1, 0}, {5}, {5}, {5}, {5}}; - // std::vector batch_fault_update{ - // {30, 1, FaultType::three_phase, FaultPhase::abc, 1, nan, nan}, {30}, {30}, {30}, {30}}; }; // auto default_model(State const& state) -> Model { return Model{50.0, state.get_input_dataset()}; } } // namespace -TEST_CASE("API Model - indexing + bad input") { - SUBCASE("Test get indexer") { - std::vector const node_id{1, 2, 3}; - std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; - - DatasetConst input_dataset{"input", 0, 1}; - input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); - input_dataset.add_attribute_buffer("node", "id", node_id.data()); - input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); - - auto model = Model{50.0, input_dataset}; - - std::vector const ids_to_index{2, 1, 3, 2}; - std::vector const expected_indexer{1, 0, 2, 1}; - std::vector indexer(ids_to_index.size()); - model.get_indexer("node", ids_to_index.size(), ids_to_index.data(), indexer.data()); - CHECK(indexer == expected_indexer); - } - - SUBCASE("Test duplicated id") { - std::vector node_id{1, 1, 3}; - DatasetConst input_dataset{"input", 0, 1}; - - input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); - input_dataset.add_attribute_buffer("node", "id", node_id.data()); - - auto construct_model = [&] { Model{50.0, input_dataset}; }; - CHECK_THROWS_WITH_AS(construct_model(), "Conflicting id detected: 1\n", PowerGridRegularError); - } - - SUBCASE("Test non-existing id") { - std::vector const node_id{1, 2, 3}; - std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; - - std::vector link_id{5}; - std::vector link_from_node{99}; - std::vector link_to_node{3}; - - DatasetConst input_dataset{"input", 0, 1}; - - input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); - input_dataset.add_attribute_buffer("node", "id", node_id.data()); - input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); - - input_dataset.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); - input_dataset.add_attribute_buffer("link", "id", link_id.data()); - input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); - input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); - - auto construct_model = [&] { Model{50.0, input_dataset}; }; - CHECK_THROWS_WITH_AS(construct_model(), "The id cannot be found: 99\n", PowerGridRegularError); - } - - SUBCASE("Test id for wrong type") { - std::vector const node_id{1, 2, 3}; - std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; - - std::vector line_id{9}; - std::vector line_from_node{1}; - std::vector line_to_node{2}; - - std::vector link_id{5}; - std::vector link_from_node{2}; - std::vector link_to_node{3}; - - std::vector sym_voltage_sensor_id{25}; - std::vector sym_voltage_sensor_measured_object{1}; - - std::vector sym_power_sensor_id{28}; - std::vector sym_power_sensor_measured_object{3}; - std::vector sym_power_sensor_measured_terminal_type{MeasuredTerminalType::node}; - - DatasetConst input_dataset{"input", 0, 1}; - - input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); - input_dataset.add_attribute_buffer("node", "id", node_id.data()); - input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); - - input_dataset.add_buffer("line", line_id.size(), line_id.size(), nullptr, nullptr); - input_dataset.add_attribute_buffer("line", "id", line_id.data()); - input_dataset.add_attribute_buffer("line", "from_node", line_from_node.data()); - input_dataset.add_attribute_buffer("line", "to_node", line_to_node.data()); - - input_dataset.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); - input_dataset.add_attribute_buffer("link", "id", link_id.data()); - input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); - input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); - - input_dataset.add_buffer("sym_voltage_sensor", sym_voltage_sensor_id.size(), sym_voltage_sensor_id.size(), - nullptr, nullptr); - input_dataset.add_attribute_buffer("sym_voltage_sensor", "id", sym_voltage_sensor_id.data()); - input_dataset.add_attribute_buffer("sym_voltage_sensor", "measured_object", - sym_voltage_sensor_measured_object.data()); - - input_dataset.add_buffer("sym_power_sensor", sym_power_sensor_id.size(), sym_power_sensor_id.size(), nullptr, - nullptr); - input_dataset.add_attribute_buffer("sym_power_sensor", "id", sym_power_sensor_id.data()); - input_dataset.add_attribute_buffer("sym_power_sensor", "measured_object", - sym_power_sensor_measured_object.data()); - input_dataset.add_attribute_buffer("sym_power_sensor", "measured_terminal_type", - sym_power_sensor_measured_terminal_type.data()); - - auto construct_model = [&] { Model{50.0, input_dataset}; }; - - SUBCASE("Correct type") { CHECK_NOTHROW(construct_model()); } - SUBCASE("Wrong branch terminal node") { - link_from_node[0] = 9; - CHECK_THROWS_WITH_AS(construct_model(), "Wrong type for object with id 9\n", PowerGridRegularError); - } - SUBCASE("Wrong voltage sensor measured object") { - sym_voltage_sensor_measured_object[0] = 5; - CHECK_THROWS_WITH_AS(construct_model(), "Wrong type for object with id 5\n", PowerGridRegularError); - } - SUBCASE("Wrong power sensor measured object type") { - using enum MeasuredTerminalType; - std::vector const mt_types{branch_from, branch_to, generator, load, shunt, source}; - - for (auto const& mt_type : mt_types) { - CAPTURE(mt_type); - sym_power_sensor_measured_terminal_type[0] = mt_type; - CHECK_THROWS_WITH_AS(construct_model(), "Wrong type for object with id 3\n", PowerGridRegularError); - } - } - } -} - TEST_CASE("API model - all updates") { using namespace std::string_literals; @@ -535,12 +370,6 @@ TEST_CASE("API model - all updates") { std::vector link_update_from_status{1}; std::vector link_update_to_status{0}; - std::vector fault_update_id{30}; - std::vector fault_update_status{1}; - std::vector fault_update_type{FaultType::three_phase}; - std::vector fault_update_phase{FaultPhase::abc}; - std::vector fault_update_object{1}; - DatasetConst update_data{"update", 1, 1}; update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); update_data.add_attribute_buffer("sym_load", "id", sym_load_update_id.data()); @@ -567,13 +396,6 @@ TEST_CASE("API model - all updates") { update_data.add_attribute_buffer("link", "from_status", link_update_from_status.data()); update_data.add_attribute_buffer("link", "to_status", link_update_to_status.data()); - update_data.add_buffer("fault", 1, 1, nullptr, nullptr); - update_data.add_attribute_buffer("fault", "id", fault_update_id.data()); - update_data.add_attribute_buffer("fault", "status", fault_update_status.data()); - update_data.add_attribute_buffer("fault", "fault_type", fault_update_type.data()); - update_data.add_attribute_buffer("fault", "fault_phase", fault_update_phase.data()); - update_data.add_attribute_buffer("fault", "fault_object", fault_update_object.data()); - auto const output_dataset_type = "sym_output"s; for (Idx comp_type_idx = 0; comp_type_idx < input_info.n_components(); ++comp_type_idx) { CAPTURE(comp_type_idx); @@ -621,7 +443,6 @@ TEST_CASE("API model - all updates") { // update_data.add_buffer("shunt", 1, state.batch_shunt_update.size(), nullptr, state.batch_shunt_update.data()); // update_data.add_buffer("source", 1, state.batch_source_update.size(), nullptr, state.batch_source_update.data()); // update_data.add_buffer("link", 1, state.batch_link_update.size(), nullptr, state.batch_link_update.data()); -// update_data.add_buffer("fault", 1, state.batch_fault_update.size(), nullptr, state.batch_fault_update.data()); // model.update_components(update_data); From 74e7e759bda91fb1c4a412d49096efc6d554bd39 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 13:37:45 +0100 Subject: [PATCH 11/49] remove duplicate test Signed-off-by: Martijn Govers --- .../native_api_tests/test_api_model_misc.cpp | 52 ------------------- 1 file changed, 52 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index 83253fdeb..09471f80b 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -429,58 +429,6 @@ TEST_CASE("API model - all updates") { } } -// TEST_CASE_TEMPLATE("Test main model - single permanent update from batch", settings, regular_update, -// cached_update) { // TODO(mgovers): we should whitebox-test this instead -// State state; -// auto model = default_model(state); - -// state.batch_sym_load_update[0].p_specified = 2.5e6; -// ConstDataset update_data{true, 5, "update", meta_data::meta_data_gen::meta_data}; -// update_data.add_buffer("sym_load", 1, state.batch_sym_load_update.size(), nullptr, -// state.batch_sym_load_update.data()); -// update_data.add_buffer("asym_load", 1, state.batch_asym_load_update.size(), nullptr, -// state.batch_asym_load_update.data()); -// update_data.add_buffer("shunt", 1, state.batch_shunt_update.size(), nullptr, state.batch_shunt_update.data()); -// update_data.add_buffer("source", 1, state.batch_source_update.size(), nullptr, state.batch_source_update.data()); -// update_data.add_buffer("link", 1, state.batch_link_update.size(), nullptr, state.batch_link_update.data()); - -// model.update_components(update_data); - -// SUBCASE("Symmetrical") { -// auto const solver_output = -// model.calculate(get_default_options(symmetric, -// CalculationMethod::linear)); -// model.output_result(solver_output, state.sym_node); -// model.output_result(solver_output, state.sym_branch); -// model.output_result(solver_output, state.sym_appliance); -// CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); -// CHECK(state.sym_node[1].u_pu == doctest::Approx(1.05)); -// CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); -// CHECK(state.sym_branch[0].i_from == doctest::Approx(0.0).epsilon(1e-6)); -// CHECK(state.sym_appliance[0].i == doctest::Approx(0.0).epsilon(1e-6)); -// CHECK(state.sym_appliance[1].i == doctest::Approx(test::i)); -// CHECK(state.sym_appliance[2].i == doctest::Approx(test::i)); -// CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); -// CHECK(state.sym_appliance[4].i == doctest::Approx(0.0)); -// } -// SUBCASE("Asymmetrical") { -// auto const solver_output = model.calculate( -// get_default_options(asymmetric, CalculationMethod::linear)); -// model.output_result(solver_output, state.asym_node); -// model.output_result(solver_output, state.asym_branch); -// model.output_result(solver_output, state.asym_appliance); -// CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); -// CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(1.05)); -// CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); -// CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(0.0).epsilon(1e-6)); -// CHECK(state.asym_appliance[0].i(1) == doctest::Approx(0.0).epsilon(1e-6)); -// CHECK(state.asym_appliance[1].i(2) == doctest::Approx(test::i)); -// CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i)); -// CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); -// CHECK(state.asym_appliance[4].i(2) == doctest::Approx(0.0)); -// } -// } - // TEST_CASE_TEMPLATE("Test main model - updates w/ alternating compute mode", settings, regular_update, // cached_update) { // TODO(mgovers): move to api tests; not possible with current validation // framework From b8799326f54dd5635082fa3c3e82a0de5489375b Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 14:20:59 +0100 Subject: [PATCH 12/49] migrate updates with alternating compute mode test Signed-off-by: Martijn Govers --- .../native_api_tests/test_api_model_misc.cpp | 207 +++++++++++------- 1 file changed, 130 insertions(+), 77 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index 09471f80b..30ce99428 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -404,10 +404,6 @@ TEST_CASE("API model - all updates") { CAPTURE(comp_type); auto const comp_meta = MetaData::get_component_by_name(output_dataset_type, comp_type); - - DatasetMutable output_data_from_batch{output_dataset_type, 1, 1}; - DatasetMutable output_data_from_updated_single{output_dataset_type, false, 1}; - auto const total_elements = input_info.component_total_elements(comp_type_idx); auto const elements_per_scenario = input_info.component_elements_per_scenario(comp_type_idx); auto const n_bytes = total_elements * MetaData::component_size(comp_meta); @@ -415,6 +411,9 @@ TEST_CASE("API model - all updates") { std::vector sym_output_from_batch(n_bytes); std::vector sym_output_from_updated_single(n_bytes); + DatasetMutable output_data_from_batch{output_dataset_type, 1, 1}; + DatasetMutable output_data_from_updated_single{output_dataset_type, false, 1}; + output_data_from_batch.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, reinterpret_cast(sym_output_from_batch.data())); output_data_from_updated_single.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, @@ -429,83 +428,137 @@ TEST_CASE("API model - all updates") { } } -// TEST_CASE_TEMPLATE("Test main model - updates w/ alternating compute mode", settings, regular_update, -// cached_update) { // TODO(mgovers): move to api tests; not possible with current validation -// framework -// constexpr auto check_sym = [](MainModel const& model_, auto const& math_output_) { -// State state_; -// model_.output_result(math_output_, state_.sym_node); -// model_.output_result(math_output_, state_.sym_branch); -// model_.output_result(math_output_, state_.sym_appliance); - -// CHECK(state_.sym_node[0].u_pu == doctest::Approx(1.05)); -// CHECK(state_.sym_node[1].u_pu == doctest::Approx(test::u1)); -// CHECK(state_.sym_node[2].u_pu == doctest::Approx(test::u1)); -// CHECK(state_.sym_branch[0].i_from == doctest::Approx(test::i)); -// CHECK(state_.sym_appliance[0].i == doctest::Approx(test::i)); -// CHECK(state_.sym_appliance[1].i == doctest::Approx(0.0)); -// CHECK(state_.sym_appliance[2].i == doctest::Approx(test::i_load * 2 + test::i_shunt)); -// CHECK(state_.sym_appliance[3].i == doctest::Approx(0.0)); -// CHECK(state_.sym_appliance[4].i == doctest::Approx(0.0)); -// }; -// constexpr auto check_asym = [](MainModel const& model_, auto const& math_output_) { -// State state_; -// model_.output_result(math_output_, state_.asym_node); -// model_.output_result(math_output_, state_.asym_branch); -// model_.output_result(math_output_, state_.asym_appliance); -// CHECK(state_.asym_node[0].u_pu(0) == doctest::Approx(1.05)); -// CHECK(state_.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); -// CHECK(state_.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); -// CHECK(state_.asym_branch[0].i_from(0) == doctest::Approx(test::i)); -// CHECK(state_.asym_appliance[0].i(1) == doctest::Approx(test::i)); -// CHECK(state_.asym_appliance[1].i(2) == doctest::Approx(0.0)); -// CHECK(state_.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2 + test::i_shunt)); -// CHECK(state_.asym_appliance[3].i(1) == doctest::Approx(0.0)); -// CHECK(state_.asym_appliance[4].i(2) == doctest::Approx(0.0)); -// }; - -// State state; -// auto model = default_model(state); +TEST_CASE("API model - updates w/ alternating compute mode") { + State state; + auto const input_dataset = state.get_input_dataset(); + auto const& input_info = input_dataset.get_info(); + auto model = Model{50.0, input_dataset}; -// state.sym_load_update[0].p_specified = 2.5e6; + auto const check_sym = [&] { + std::vector sym_node_output_u_pu(3); + std::vector sym_line_output_i_from(1); + std::vector sym_source_output_i(2); + std::vector sym_sym_load_output_i(1); + std::vector sym_asym_load_output_i(1); + std::vector sym_shunt_output_i(1); -// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; -// update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, -// state.sym_load_update.data()); -// update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, -// state.asym_load_update.data()); -// update_data.add_buffer("shunt", state.shunt_update.size(), state.shunt_update.size(), nullptr, -// state.shunt_update.data()); - -// // This will lead to no topo change but param change -// model.update_components(update_data); - -// auto const math_output_sym_1 = -// model.calculate(get_default_options(symmetric, CalculationMethod::linear)); -// check_sym(model, math_output_sym_1); - -// auto const math_output_asym_1 = -// model.calculate(get_default_options(asymmetric, CalculationMethod::linear)); -// check_asym(model, math_output_asym_1); - -// SUBCASE("No new update") { -// // Math state may be fully cached -// } -// if constexpr (std::same_as) { -// SUBCASE("No new parameter change") { -// // Math state may be fully cached due to no change -// model.update_components(update_data); -// } -// } + DatasetMutable sym_output{"sym_output", false, 1}; + sym_output.add_buffer("node", 1, 1, nullptr, nullptr); + sym_output.add_attribute_buffer("node", "u_pu", reinterpret_cast(sym_node_output_u_pu.data())); -// auto const math_output_asym_2 = -// model.calculate(get_default_options(asymmetric, CalculationMethod::linear)); -// check_asym(model, math_output_asym_2); + sym_output.add_buffer("line", 1, 1, nullptr, nullptr); + sym_output.add_attribute_buffer("line", "i_from", reinterpret_cast(sym_line_output_i_from.data())); -// auto const math_output_sym_2 = -// model.calculate(get_default_options(symmetric, CalculationMethod::linear)); -// check_sym(model, math_output_sym_2); -// } + sym_output.add_buffer("source", 2, 2, nullptr, nullptr); + sym_output.add_attribute_buffer("source", "i", reinterpret_cast(sym_source_output_i.data())); + + sym_output.add_buffer("sym_load", 1, 1, nullptr, nullptr); + sym_output.add_attribute_buffer("sym_load", "i", reinterpret_cast(sym_sym_load_output_i.data())); + + sym_output.add_buffer("asym_load", 1, 1, nullptr, nullptr); + sym_output.add_attribute_buffer("asym_load", "i", reinterpret_cast(sym_asym_load_output_i.data())); + + sym_output.add_buffer("shunt", 1, 1, nullptr, nullptr); + sym_output.add_attribute_buffer("shunt", "i", reinterpret_cast(sym_shunt_output_i.data())); + + model.calculate(get_default_options(PGM_symmetric, PGM_linear), sym_output); + + CHECK(sym_node_output_u_pu[0] == doctest::Approx(1.05)); + CHECK(sym_node_output_u_pu[1] == doctest::Approx(test::u1)); + CHECK(sym_node_output_u_pu[2] == doctest::Approx(test::u1)); + CHECK(sym_line_output_i_from[0] == doctest::Approx(test::i)); + CHECK(sym_source_output_i[0] == doctest::Approx(test::i)); + CHECK(sym_source_output_i[1] == doctest::Approx(0.0)); + CHECK(sym_sym_load_output_i[0] == doctest::Approx(test::i_load * 2 + test::i_shunt)); + CHECK(sym_asym_load_output_i[0] == doctest::Approx(0.0)); + CHECK(sym_shunt_output_i[0] == doctest::Approx(0.0)); + }; + auto const check_asym = [&] { + std::vector asym_node_output_u_pu(3 * 3); + std::vector asym_line_output_i_from(1 * 3); + std::vector asym_source_output_i(2 * 3); + std::vector asym_sym_load_output_i(1 * 3); + std::vector asym_asym_load_output_i(1 * 3); + std::vector asym_shunt_output_i(1 * 3); + + DatasetMutable asym_output{"asym_output", false, 1}; + asym_output.add_buffer("node", 1, 1, nullptr, nullptr); + asym_output.add_attribute_buffer("node", "u_pu", reinterpret_cast(asym_node_output_u_pu.data())); + + asym_output.add_buffer("line", 1, 1, nullptr, nullptr); + asym_output.add_attribute_buffer("line", "i_from", reinterpret_cast(asym_line_output_i_from.data())); + + asym_output.add_buffer("source", 2, 2, nullptr, nullptr); + asym_output.add_attribute_buffer("source", "i", reinterpret_cast(asym_source_output_i.data())); + + asym_output.add_buffer("sym_load", 1, 1, nullptr, nullptr); + asym_output.add_attribute_buffer("sym_load", "i", reinterpret_cast(asym_sym_load_output_i.data())); + + asym_output.add_buffer("asym_load", 1, 1, nullptr, nullptr); + asym_output.add_attribute_buffer("asym_load", "i", reinterpret_cast(asym_asym_load_output_i.data())); + + asym_output.add_buffer("shunt", 1, 1, nullptr, nullptr); + asym_output.add_attribute_buffer("shunt", "i", reinterpret_cast(asym_shunt_output_i.data())); + + model.calculate(get_default_options(PGM_asymmetric, PGM_linear), asym_output); + + CHECK(asym_node_output_u_pu[0 * 3 + 0] == doctest::Approx(1.05)); + CHECK(asym_node_output_u_pu[1 * 3 + 1] == doctest::Approx(test::u1)); + CHECK(asym_node_output_u_pu[2 * 2 + 1] == doctest::Approx(test::u1)); + CHECK(asym_line_output_i_from[0] == doctest::Approx(test::i)); + CHECK(asym_source_output_i[0 * 3 + 1] == doctest::Approx(test::i)); + CHECK(asym_source_output_i[1 * 3 + 2] == doctest::Approx(0.0)); + CHECK(asym_sym_load_output_i[0] == doctest::Approx(test::i_load * 2 + test::i_shunt)); + CHECK(asym_asym_load_output_i[1] == doctest::Approx(0.0)); + CHECK(asym_shunt_output_i[2] == doctest::Approx(0.0)); + }; + + // update vector + std::vector sym_load_update_id{7}; + std::vector sym_load_update_status{1}; + std::vector sym_load_update_p_specified{2.5e6}; + + std::vector asym_load_update_id{8}; + std::vector asym_load_update_status{0}; + + std::vector shunt_update_id{9}; + std::vector shunt_update_status{0}; + std::vector shunt_update_b1{0.02}; + std::vector shunt_update_b0{0.02}; + + DatasetConst update_data{"update", 1, 1}; + update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("sym_load", "id", sym_load_update_id.data()); + update_data.add_attribute_buffer("sym_load", "status", sym_load_update_status.data()); + update_data.add_attribute_buffer("sym_load", "p_specified", sym_load_update_p_specified.data()); + + update_data.add_buffer("asym_load", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("asym_load", "id", asym_load_update_id.data()); + update_data.add_attribute_buffer("asym_load", "status", asym_load_update_status.data()); + + update_data.add_buffer("shunt", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("shunt", "id", shunt_update_id.data()); + update_data.add_attribute_buffer("shunt", "status", shunt_update_status.data()); + update_data.add_attribute_buffer("shunt", "b1", shunt_update_b1.data()); + update_data.add_attribute_buffer("shunt", "b0", shunt_update_b0.data()); + + // This will lead to no topo change but param change + model.update(update_data); + + check_sym(); + check_asym(); + + SUBCASE("No new update") { + // Math state may be fully cached + } + SUBCASE("No new parameter change") { + // Math state may be fully cached + model.update(update_data); + } + + check_asym(); + check_sym(); +} // namespace { // auto incomplete_input_model(State const& state) -> MainModel { From 69590afe57e432e6aac550234f26116df9571a90 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 14:47:33 +0100 Subject: [PATCH 13/49] remove redundant code Signed-off-by: Martijn Govers --- .../native_api_tests/test_api_model_misc.cpp | 136 ++---------------- 1 file changed, 14 insertions(+), 122 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index 30ce99428..5126d4c7e 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -125,54 +126,6 @@ struct State { std::vector shunt_g0{0.015}; std::vector shunt_b0{0.0}; - std::vector sym_power_sensor_id{11, 13, 14, 15, 16, 17, 28}; - std::vector sym_power_sensor_measured_object{4, 6, 6, 9, 7, 8, 3}; - std::vector sym_power_sensor_measured_terminal_type{ - MeasuredTerminalType::branch_from, MeasuredTerminalType::source, MeasuredTerminalType::source, - MeasuredTerminalType::shunt, MeasuredTerminalType::load, MeasuredTerminalType::load, - MeasuredTerminalType::node}; - std::vector sym_power_sensor_power_sigma{0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02}; - std::vector sym_power_sensor_p_measured{1.1e6, 1.3e6, 1.4e6, 1.5e6, 1.6e6, 1.7e6, 3.0e6}; - std::vector sym_power_sensor_q_measured{1.1e3, 1.3e3, 1.4e3, 1.5e3, 1.6e3, 1.7e3, 3.0e3}; - - std::vector asym_power_sensor_id{18, 20, 21, 22, 23, 24, 29}; - std::vector asym_power_sensor_measured_object{4, 6, 6, 9, 7, 8, 3}; - std::vector asym_power_sensor_measured_terminal_type{ - MeasuredTerminalType::branch_from, MeasuredTerminalType::source, MeasuredTerminalType::source, - MeasuredTerminalType::shunt, MeasuredTerminalType::load, MeasuredTerminalType::load, - MeasuredTerminalType::node}; - std::vector asym_power_sensor_power_sigma{0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02}; - std::vector asym_power_sensor_p_measured{ - 2.11e6, 2.12e6, 2.13e6, // 18 - 2.31e6, 2.32e6, 2.33e6, // 20 - 2.41e6, 2.42e6, 2.43e6, // 21 - 2.51e6, 2.52e6, 2.53e6, // 22 - 2.61e6, 2.62e6, 2.63e6, // 23 - 2.71e6, 2.72e6, 2.73e6, // 24 - 5.01e6, 5.02e6, 5.03e6 // 28 - }; - std::vector asym_power_sensor_q_measured{ - 2.11e3, 2.12e3, 2.13e3, // 18 - 2.31e3, 2.32e3, 2.33e3, // 20 - 2.41e3, 2.42e3, 2.43e3, // 21 - 2.51e3, 2.52e3, 2.53e3, // 22 - 2.61e3, 2.62e3, 2.63e3, // 23 - 2.71e3, 2.72e3, 2.73e3, // 24 - 5.01e3, 5.02e3, 5.03e3, // 29 - }; - - std::vector sym_voltage_sensor_id{25, 26}; - std::vector sym_voltage_sensor_measured_object{1, 2}; - std::vector sym_voltage_sensor_u_sigma{105.0, 105.0}; - std::vector sym_voltage_sensor_u_measured{10.1e3, 10.2e3}; - std::vector sym_voltage_sensor_u_angle_measured{0.1, 0.2}; - - std::vector asym_voltage_sensor_id{27}; - std::vector asym_voltage_sensor_measured_object{3}; - std::vector asym_voltage_sensor_u_sigma{105.0}; - std::vector asym_voltage_sensor_u_measured{10.31e3 / sqrt3, 10.32e3 / sqrt3, 10.33e3 / sqrt3}; - std::vector asym_voltage_sensor_u_angle_measured{0.0, -deg_120, -deg_240}; - auto get_input_dataset() const { DatasetConst result{"input", 0, 1}; @@ -236,44 +189,6 @@ struct State { result.add_attribute_buffer("shunt", "g0", shunt_g0.data()); result.add_attribute_buffer("shunt", "b0", shunt_b0.data()); - result.add_buffer("sym_power_sensor", sym_power_sensor_id.size(), sym_power_sensor_id.size(), nullptr, nullptr); - result.add_attribute_buffer("sym_power_sensor", "id", sym_power_sensor_id.data()); - result.add_attribute_buffer("sym_power_sensor", "measured_object", sym_power_sensor_measured_object.data()); - result.add_attribute_buffer("sym_power_sensor", "measured_terminal_type", - sym_power_sensor_measured_terminal_type.data()); - result.add_attribute_buffer("sym_power_sensor", "power_sigma", sym_power_sensor_power_sigma.data()); - result.add_attribute_buffer("sym_power_sensor", "p_measured", sym_power_sensor_p_measured.data()); - result.add_attribute_buffer("sym_power_sensor", "q_measured", sym_power_sensor_q_measured.data()); - - result.add_buffer("asym_power_sensor", asym_power_sensor_id.size(), asym_power_sensor_id.size(), nullptr, - nullptr); - result.add_attribute_buffer("asym_power_sensor", "id", asym_power_sensor_id.data()); - result.add_attribute_buffer("asym_power_sensor", "measured_object", asym_power_sensor_measured_object.data()); - result.add_attribute_buffer("asym_power_sensor", "measured_terminal_type", - asym_power_sensor_measured_terminal_type.data()); - result.add_attribute_buffer("asym_power_sensor", "power_sigma", asym_power_sensor_power_sigma.data()); - result.add_attribute_buffer("asym_power_sensor", "p_measured", asym_power_sensor_p_measured.data()); - result.add_attribute_buffer("asym_power_sensor", "q_measured", asym_power_sensor_q_measured.data()); - - result.add_buffer("sym_voltage_sensor", sym_voltage_sensor_id.size(), sym_voltage_sensor_id.size(), nullptr, - nullptr); - result.add_attribute_buffer("sym_voltage_sensor", "id", sym_voltage_sensor_id.data()); - result.add_attribute_buffer("sym_voltage_sensor", "measured_object", sym_voltage_sensor_measured_object.data()); - result.add_attribute_buffer("sym_voltage_sensor", "u_sigma", sym_voltage_sensor_u_sigma.data()); - result.add_attribute_buffer("sym_voltage_sensor", "u_measured", sym_voltage_sensor_u_measured.data()); - result.add_attribute_buffer("sym_voltage_sensor", "u_angle_measured", - sym_voltage_sensor_u_angle_measured.data()); - - result.add_buffer("asym_voltage_sensor", asym_voltage_sensor_id.size(), asym_voltage_sensor_id.size(), nullptr, - nullptr); - result.add_attribute_buffer("asym_voltage_sensor", "id", asym_voltage_sensor_id.data()); - result.add_attribute_buffer("asym_voltage_sensor", "measured_object", - asym_voltage_sensor_measured_object.data()); - result.add_attribute_buffer("asym_voltage_sensor", "u_sigma", asym_voltage_sensor_u_sigma.data()); - result.add_attribute_buffer("asym_voltage_sensor", "u_measured", asym_voltage_sensor_u_measured.data()); - result.add_attribute_buffer("asym_voltage_sensor", "u_angle_measured", - asym_voltage_sensor_u_angle_measured.data()); - return result; } @@ -292,13 +207,6 @@ struct State { // std::vector> sym_load_asym = std::vector>(1); // std::vector> sym_source = std::vector>(2); // std::vector> sym_shunt = std::vector>(1); - // std::vector> sym_voltage_sensor_sensor = - // std::vector>(2); std::vector> - // asym_voltage_sensor_sensor_sym_output = - // std::vector>(1); - // std::vector> sym_power_sensor = std::vector>(7); - // std::vector> asym_power_sensor_sym_output = - // std::vector>(7); // // individual asymmetric // std::vector> asym_line = std::vector>(1); @@ -307,13 +215,6 @@ struct State { // std::vector> asym_load_asym = std::vector>(1); // std::vector> asym_source = std::vector>(2); // std::vector> asym_shunt = std::vector>(1); - // std::vector> asym_voltage_sensor_sensor = - // std::vector>(1); - // std::vector> sym_voltage_sensor_sensor_asym_output = - // std::vector>(2); - // std::vector> asym_power_sensor = std::vector>(7); - // std::vector> sym_power_sensor_asym_output = - // std::vector>(7); // // update vector // std::vector sym_load_update{{7, 1, 1.0e6, nan}}; @@ -560,28 +461,19 @@ TEST_CASE("API model - updates w/ alternating compute mode") { check_sym(); } -// namespace { -// auto incomplete_input_model(State const& state) -> MainModel { -// MainModel model{50.0, meta_data::meta_data_gen::meta_data}; - -// std::vector const incomplete_source_input{{6, 1, 1, nan, nan, 1e12, nan, nan}, -// {10, 3, 1, nan, nan, 1e12, nan, nan}}; -// std::vector const incomplete_sym_load_input{{7, 3, 1, LoadGenType::const_y, nan, 0.0}}; -// std::vector const incomplete_asym_load_input{ -// {8, 3, 1, LoadGenType::const_y, RealValue{nan}, RealValue{0.0}}}; - -// model.add_component(state.node_input); -// model.add_component(state.line_input); -// model.add_component(state.link_input); -// model.add_component(incomplete_source_input); -// model.add_component(incomplete_sym_load_input); -// model.add_component(incomplete_asym_load_input); -// model.add_component(state.shunt_input); -// model.set_construction_complete(); - -// return model; -// } -// } // namespace +namespace { +auto incomplete_state() -> State { + State result; + + std::ranges::fill(result.source_status, 1); + std::ranges::fill(result.source_u_ref, nan); + std::ranges::fill(result.source_u_ref_angle, nan); + std::ranges::fill(result.sym_load_p_specified, nan); + std::ranges::fill(result.asym_load_p_specified, nan); + + return result; +} +} // namespace // TEST_CASE("Test main model - incomplete input") { // using CalculationMethod::iterative_current; From c3fd36d97c3fa0b0a759b9733240fb24f6960a14 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 15:04:08 +0100 Subject: [PATCH 14/49] remove more redundant tests from test api model misc Signed-off-by: Martijn Govers --- .../native_api_tests/test_api_model_misc.cpp | 539 ++++++++---------- 1 file changed, 250 insertions(+), 289 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index 5126d4c7e..2a3ac5624 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -316,9 +316,9 @@ TEST_CASE("API model - all updates") { DatasetMutable output_data_from_updated_single{output_dataset_type, false, 1}; output_data_from_batch.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, - reinterpret_cast(sym_output_from_batch.data())); + sym_output_from_batch.data()); output_data_from_updated_single.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, - reinterpret_cast(sym_output_from_updated_single.data())); + sym_output_from_updated_single.data()); auto opt = get_default_options(PGM_symmetric, PGM_linear); model.calculate(opt, output_data_from_batch, update_data); @@ -345,22 +345,22 @@ TEST_CASE("API model - updates w/ alternating compute mode") { DatasetMutable sym_output{"sym_output", false, 1}; sym_output.add_buffer("node", 1, 1, nullptr, nullptr); - sym_output.add_attribute_buffer("node", "u_pu", reinterpret_cast(sym_node_output_u_pu.data())); + sym_output.add_attribute_buffer("node", "u_pu", sym_node_output_u_pu.data()); sym_output.add_buffer("line", 1, 1, nullptr, nullptr); - sym_output.add_attribute_buffer("line", "i_from", reinterpret_cast(sym_line_output_i_from.data())); + sym_output.add_attribute_buffer("line", "i_from", sym_line_output_i_from.data()); sym_output.add_buffer("source", 2, 2, nullptr, nullptr); - sym_output.add_attribute_buffer("source", "i", reinterpret_cast(sym_source_output_i.data())); + sym_output.add_attribute_buffer("source", "i", sym_source_output_i.data()); sym_output.add_buffer("sym_load", 1, 1, nullptr, nullptr); - sym_output.add_attribute_buffer("sym_load", "i", reinterpret_cast(sym_sym_load_output_i.data())); + sym_output.add_attribute_buffer("sym_load", "i", sym_sym_load_output_i.data()); sym_output.add_buffer("asym_load", 1, 1, nullptr, nullptr); - sym_output.add_attribute_buffer("asym_load", "i", reinterpret_cast(sym_asym_load_output_i.data())); + sym_output.add_attribute_buffer("asym_load", "i", sym_asym_load_output_i.data()); sym_output.add_buffer("shunt", 1, 1, nullptr, nullptr); - sym_output.add_attribute_buffer("shunt", "i", reinterpret_cast(sym_shunt_output_i.data())); + sym_output.add_attribute_buffer("shunt", "i", sym_shunt_output_i.data()); model.calculate(get_default_options(PGM_symmetric, PGM_linear), sym_output); @@ -384,22 +384,22 @@ TEST_CASE("API model - updates w/ alternating compute mode") { DatasetMutable asym_output{"asym_output", false, 1}; asym_output.add_buffer("node", 1, 1, nullptr, nullptr); - asym_output.add_attribute_buffer("node", "u_pu", reinterpret_cast(asym_node_output_u_pu.data())); + asym_output.add_attribute_buffer("node", "u_pu", asym_node_output_u_pu.data()); asym_output.add_buffer("line", 1, 1, nullptr, nullptr); - asym_output.add_attribute_buffer("line", "i_from", reinterpret_cast(asym_line_output_i_from.data())); + asym_output.add_attribute_buffer("line", "i_from", asym_line_output_i_from.data()); asym_output.add_buffer("source", 2, 2, nullptr, nullptr); - asym_output.add_attribute_buffer("source", "i", reinterpret_cast(asym_source_output_i.data())); + asym_output.add_attribute_buffer("source", "i", asym_source_output_i.data()); asym_output.add_buffer("sym_load", 1, 1, nullptr, nullptr); - asym_output.add_attribute_buffer("sym_load", "i", reinterpret_cast(asym_sym_load_output_i.data())); + asym_output.add_attribute_buffer("sym_load", "i", asym_sym_load_output_i.data()); asym_output.add_buffer("asym_load", 1, 1, nullptr, nullptr); - asym_output.add_attribute_buffer("asym_load", "i", reinterpret_cast(asym_asym_load_output_i.data())); + asym_output.add_attribute_buffer("asym_load", "i", asym_asym_load_output_i.data()); asym_output.add_buffer("shunt", 1, 1, nullptr, nullptr); - asym_output.add_attribute_buffer("shunt", "i", reinterpret_cast(asym_shunt_output_i.data())); + asym_output.add_attribute_buffer("shunt", "i", asym_shunt_output_i.data()); model.calculate(get_default_options(PGM_asymmetric, PGM_linear), asym_output); @@ -462,10 +462,10 @@ TEST_CASE("API model - updates w/ alternating compute mode") { } namespace { -auto incomplete_state() -> State { +auto get_incomplete_state() -> State { State result; - std::ranges::fill(result.source_status, 1); + // std::ranges::fill(result.source_status, 1); // TODO(mgovers): remove std::ranges::fill(result.source_u_ref, nan); std::ranges::fill(result.source_u_ref_angle, nan); std::ranges::fill(result.sym_load_p_specified, nan); @@ -475,278 +475,239 @@ auto incomplete_state() -> State { } } // namespace -// TEST_CASE("Test main model - incomplete input") { -// using CalculationMethod::iterative_current; -// using CalculationMethod::linear; -// using CalculationMethod::linear_current; -// using CalculationMethod::newton_raphson; - -// State const state; -// auto model = default_model(state); -// auto test_model = incomplete_input_model(state); - -// std::vector complete_source_update{{6, 1, 1.05, nan}, {10, 1, 1.05, 0}}; -// std::vector complete_sym_load_update{{7, 1, 0.5e6, nan}}; -// std::vector complete_asym_load_update{ -// {8, 1, RealValue{0.5e6 / 3.0}, RealValue{nan}}}; - -// ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; -// update_data.add_buffer("source", complete_source_update.size(), complete_source_update.size(), nullptr, -// complete_source_update.data()); -// update_data.add_buffer("sym_load", complete_sym_load_update.size(), complete_sym_load_update.size(), nullptr, -// complete_sym_load_update.data()); -// update_data.add_buffer("asym_load", complete_asym_load_update.size(), complete_asym_load_update.size(), nullptr, -// complete_asym_load_update.data()); - -// std::vector incomplete_source_update{{6, na_IntS, nan, nan}, {10, na_IntS, nan, nan}}; -// std::vector incomplete_sym_load_update{{7, na_IntS, nan, nan}}; -// std::vector incomplete_asym_load_update{ -// {8, na_IntS, RealValue{nan}, RealValue{nan}}}; - -// ConstDataset incomplete_update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; -// incomplete_update_data.add_buffer("source", incomplete_source_update.size(), incomplete_source_update.size(), -// nullptr, incomplete_source_update.data()); -// incomplete_update_data.add_buffer("sym_load", incomplete_sym_load_update.size(), -// incomplete_sym_load_update.size(), -// nullptr, incomplete_sym_load_update.data()); -// incomplete_update_data.add_buffer("asym_load", incomplete_asym_load_update.size(), -// incomplete_asym_load_update.size(), nullptr, -// incomplete_asym_load_update.data()); - -// MainModel const ref_model{model}; - -// SUBCASE("Asymmetrical - Complete") { // TODO(mgovers): no validation case for asym exists -// MutableDataset test_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; -// MutableDataset ref_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; - -// std::vector> test_asym_node(state.asym_node.size()); -// std::vector> ref_asym_node(state.asym_node.size()); -// test_result_data.add_buffer("node", test_asym_node.size(), test_asym_node.size(), nullptr, -// test_asym_node.data()); -// ref_result_data.add_buffer("node", ref_asym_node.size(), ref_asym_node.size(), nullptr, -// ref_asym_node.data()); - -// SUBCASE("Test linear calculation") { -// test_model.calculate(get_default_options(asymmetric, linear), test_result_data, update_data); -// model.calculate(get_default_options(asymmetric, linear), ref_result_data, update_data); -// } - -// SUBCASE("Test linear current calculation") { -// test_model.calculate(get_default_options(asymmetric, linear_current), test_result_data, update_data); -// model.calculate(get_default_options(asymmetric, linear_current), ref_result_data, update_data); -// } - -// SUBCASE("Test iterative current calculation") { -// test_model.calculate(get_default_options(asymmetric, iterative_current), test_result_data, update_data); -// model.calculate(get_default_options(asymmetric, iterative_current), ref_result_data, update_data); -// } - -// SUBCASE("Test iterative Newton-Rhapson calculation") { -// test_model.calculate(get_default_options(asymmetric, newton_raphson), test_result_data, update_data); -// model.calculate(get_default_options(asymmetric, newton_raphson), ref_result_data, update_data); -// } - -// for (auto component_idx : {0, 1, 2}) { -// CAPTURE(component_idx); - -// for (auto phase_idx : {0, 1, 2}) { -// CAPTURE(phase_idx); - -// CHECK(test_asym_node[component_idx].u_pu(phase_idx) == -// doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx))); -// } -// } -// } - -// SUBCASE("Symmetrical - Incomplete") { // TODO(mgovers): not tested elsewhere; maybe test in API model? -// MutableDataset test_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; -// MutableDataset const ref_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - -// std::vector> test_sym_node(state.sym_node.size()); -// test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, -// test_sym_node.data()); - -// SUBCASE("Target dataset") { -// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = symmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// test_result_data), -// SparseMatrixError); -// } -// SUBCASE("Empty update dataset") { -// ConstDataset const update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - -// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = symmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// test_result_data, update_data), -// SparseMatrixError); -// } -// SUBCASE("Update dataset") { -// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = symmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// test_result_data, incomplete_update_data), -// BatchCalculationError); -// } -// } - -// SUBCASE("Asymmetrical - Incomplete") { // TODO(mgovers): not tested elsewhere; maybe test in API model? -// MutableDataset test_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; -// MutableDataset const ref_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; - -// std::vector> test_sym_node(state.sym_node.size()); -// test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, -// test_sym_node.data()); - -// SUBCASE("Target dataset") { -// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = asymmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// test_result_data), -// SparseMatrixError); -// } -// SUBCASE("Empty update dataset") { -// ConstDataset const update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - -// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = asymmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// test_result_data, update_data), -// SparseMatrixError); -// } -// SUBCASE("Update dataset") { -// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = asymmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// test_result_data, incomplete_update_data), -// BatchCalculationError); -// } -// } -// } - -// TEST_CASE("Test main model - Incomplete followed by complete") { // TODO(mgovers): This tests the reset of 2 -// consecutive -// // batch scenarios and definitely needs to be tested -// using CalculationMethod::linear; - -// State const state; -// auto model = default_model(state); -// auto test_model = incomplete_input_model(state); - -// constexpr Idx batch_size = 2; - -// std::vector mixed_source_update{ -// {6, 1, nan, nan}, {10, 1, nan, nan}, {6, 1, 1.05, nan}, {10, 1, 1.05, 0}}; -// std::vector mixed_sym_load_update{{7, 1, nan, 1.0}, {7, 1, 0.5e6, nan}}; -// std::vector mixed_asym_load_update{ -// {8, 1, RealValue{nan}, RealValue{1.0}}, -// {8, 1, RealValue{0.5e6 / 3.0}, RealValue{nan}}}; - -// auto const source_indptr = IdxVector{0, 0, static_cast(mixed_source_update.size())}; - -// REQUIRE(source_indptr.size() == batch_size + 1); - -// ConstDataset mixed_update_data{true, batch_size, "update", meta_data::meta_data_gen::meta_data}; -// mixed_update_data.add_buffer("source", 2, 4, nullptr, mixed_source_update.data()); -// mixed_update_data.add_buffer("sym_load", 1, 2, nullptr, mixed_sym_load_update.data()); -// mixed_update_data.add_buffer("asym_load", 1, 2, nullptr, mixed_asym_load_update.data()); - -// ConstDataset second_scenario_update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; -// second_scenario_update_data.add_buffer("source", 2, 2, nullptr, mixed_source_update.data() + 2); -// second_scenario_update_data.add_buffer("sym_load", 1, 1, nullptr, mixed_sym_load_update.data() + 1); -// second_scenario_update_data.add_buffer("asym_load", 1, 1, nullptr, mixed_asym_load_update.data() + 1); - -// SUBCASE("Symmetrical") { -// MutableDataset test_result_data{true, batch_size, "sym_output", meta_data::meta_data_gen::meta_data}; -// MutableDataset ref_result_data{false, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - -// std::vector> test_sym_node(batch_size * state.sym_node.size(), -// {na_IntID, na_IntS, nan, nan, nan, nan, nan}); -// std::vector> ref_sym_node(state.sym_node.size(), -// {na_IntID, na_IntS, nan, nan, nan, nan, nan}); -// test_result_data.add_buffer("node", state.sym_node.size(), test_sym_node.size(), nullptr, -// test_sym_node.data()); ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), nullptr, -// ref_sym_node.data()); - -// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = symmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// test_result_data, mixed_update_data), -// BatchCalculationError); -// model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = symmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// ref_result_data, second_scenario_update_data); - -// CHECK(is_nan(test_sym_node[0].u_pu)); -// CHECK(is_nan(test_sym_node[1].u_pu)); -// CHECK(is_nan(test_sym_node[2].u_pu)); -// CHECK(test_sym_node[state.sym_node.size() + 0].u_pu == doctest::Approx(ref_sym_node[0].u_pu)); -// CHECK(test_sym_node[state.sym_node.size() + 1].u_pu == doctest::Approx(ref_sym_node[1].u_pu)); -// CHECK(test_sym_node[state.sym_node.size() + 2].u_pu == doctest::Approx(ref_sym_node[2].u_pu)); -// } - -// SUBCASE("Asymmetrical") { -// MutableDataset test_result_data{true, batch_size, "asym_output", meta_data::meta_data_gen::meta_data}; -// MutableDataset ref_result_data{false, 1, "asym_output", meta_data::meta_data_gen::meta_data}; - -// std::vector> test_asym_node( -// batch_size * state.sym_node.size(), -// {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, -// RealValue{nan}, RealValue{nan}, RealValue{nan}}); -// std::vector> ref_asym_node( -// state.sym_node.size(), -// {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, -// RealValue{nan}, RealValue{nan}, RealValue{nan}}); -// test_result_data.add_buffer("node", state.sym_node.size(), test_asym_node.size(), nullptr, -// test_asym_node.data()); -// ref_result_data.add_buffer("node", ref_asym_node.size(), ref_asym_node.size(), nullptr, -// ref_asym_node.data()); - -// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = asymmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// test_result_data, mixed_update_data), -// BatchCalculationError); -// model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = asymmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// ref_result_data, second_scenario_update_data); - -// for (auto component_idx : {0, 1, 2}) { -// CAPTURE(component_idx); - -// CHECK(is_nan(test_asym_node[component_idx].u_pu)); - -// for (auto phase_idx : {0, 1, 2}) { -// CAPTURE(phase_idx); - -// CHECK(test_asym_node[state.asym_node.size() + component_idx].u_pu(phase_idx) == -// doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx))); -// } -// } -// } -// } +TEST_CASE("API model - incomplete input") { + State complete_state; + State incomplete_state = get_incomplete_state(); + + auto model = Model{50.0, complete_state.get_input_dataset()}; + auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; + + DatasetConst complete_update_data{"update", false, 1}; + complete_update_data.add_buffer("source", complete_state.source_id.size(), complete_state.source_id.size(), nullptr, + nullptr); + complete_update_data.add_attribute_buffer("source", "id", complete_state.source_id.data()); + complete_update_data.add_attribute_buffer("source", "u_ref", complete_state.source_u_ref.data()); + complete_update_data.add_attribute_buffer("source", "u_ref_angle", complete_state.source_u_ref_angle.data()); + + complete_update_data.add_buffer("sym_load", complete_state.sym_load_id.size(), complete_state.sym_load_id.size(), + nullptr, nullptr); + complete_update_data.add_attribute_buffer("sym_load", "id", complete_state.sym_load_id.data()); + complete_update_data.add_attribute_buffer("sym_load", "p_specified", complete_state.sym_load_p_specified.data()); + + complete_update_data.add_buffer("asym_load", complete_state.asym_load_id.size(), complete_state.asym_load_id.size(), + nullptr, nullptr); + complete_update_data.add_attribute_buffer("asym_load", "id", complete_state.asym_load_id.data()); + complete_update_data.add_attribute_buffer("asym_load", "p_specified", complete_state.asym_load_p_specified.data()); + + DatasetConst incomplete_update_data{"update", false, 1}; + incomplete_update_data.add_buffer("source", incomplete_state.source_id.size(), incomplete_state.source_id.size(), + nullptr, nullptr); + incomplete_update_data.add_attribute_buffer("source", "id", incomplete_state.source_id.data()); + incomplete_update_data.add_attribute_buffer("source", "u_ref", incomplete_state.source_u_ref.data()); + incomplete_update_data.add_attribute_buffer("source", "u_ref_angle", incomplete_state.source_u_ref_angle.data()); + + incomplete_update_data.add_buffer("sym_load", incomplete_state.sym_load_id.size(), + incomplete_state.sym_load_id.size(), nullptr, nullptr); + incomplete_update_data.add_attribute_buffer("sym_load", "id", incomplete_state.sym_load_id.data()); + incomplete_update_data.add_attribute_buffer("sym_load", "p_specified", + incomplete_state.sym_load_p_specified.data()); + + incomplete_update_data.add_buffer("asym_load", incomplete_state.asym_load_id.size(), + incomplete_state.asym_load_id.size(), nullptr, nullptr); + incomplete_update_data.add_attribute_buffer("asym_load", "id", incomplete_state.asym_load_id.data()); + incomplete_update_data.add_attribute_buffer("asym_load", "p_specified", + incomplete_state.asym_load_p_specified.data()); + + // SUBCASE("Symmetrical - Incomplete") { // TODO(mgovers): not tested elsewhere; maybe test in API model? + // MutableDataset test_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; + // MutableDataset const ref_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; + + // std::vector> test_sym_node(state.sym_node.size()); + // test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, + // test_sym_node.data()); + + // SUBCASE("Target dataset") { + // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, + // .calculation_symmetry = symmetric, + // .calculation_method = linear, + // .err_tol = 1e-8, + // .max_iter = 1}, + // test_result_data), + // SparseMatrixError); + // } + // SUBCASE("Empty update dataset") { + // ConstDataset const update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; + + // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, + // .calculation_symmetry = symmetric, + // .calculation_method = linear, + // .err_tol = 1e-8, + // .max_iter = 1}, + // test_result_data, update_data), + // SparseMatrixError); + // } + // SUBCASE("Update dataset") { + // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, + // .calculation_symmetry = symmetric, + // .calculation_method = linear, + // .err_tol = 1e-8, + // .max_iter = 1}, + // test_result_data, incomplete_update_data), + // BatchCalculationError); + // } + // } + + // SUBCASE("Asymmetrical - Incomplete") { // TODO(mgovers): not tested elsewhere; maybe test in API model? + // MutableDataset test_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; + // MutableDataset const ref_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; + + // std::vector> test_sym_node(state.sym_node.size()); + // test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, + // test_sym_node.data()); + + // SUBCASE("Target dataset") { + // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, + // .calculation_symmetry = asymmetric, + // .calculation_method = linear, + // .err_tol = 1e-8, + // .max_iter = 1}, + // test_result_data), + // SparseMatrixError); + // } + // SUBCASE("Empty update dataset") { + // ConstDataset const update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; + + // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, + // .calculation_symmetry = asymmetric, + // .calculation_method = linear, + // .err_tol = 1e-8, + // .max_iter = 1}, + // test_result_data, update_data), + // SparseMatrixError); + // } + // SUBCASE("Update dataset") { + // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, + // .calculation_symmetry = asymmetric, + // .calculation_method = linear, + // .err_tol = 1e-8, + // .max_iter = 1}, + // test_result_data, incomplete_update_data), + // BatchCalculationError); + // } + // } + // } + + // TEST_CASE("Test main model - Incomplete followed by complete") { // TODO(mgovers): This tests the reset of 2 + // consecutive + // // batch scenarios and definitely needs to be + // tested + // using CalculationMethod::linear; + + // State const state; + // auto model = default_model(state); + // auto test_model = incomplete_input_model(state); + + // constexpr Idx batch_size = 2; + + // std::vector mixed_source_update{ + // {6, 1, nan, nan}, {10, 1, nan, nan}, {6, 1, 1.05, nan}, {10, 1, 1.05, 0}}; + // std::vector mixed_sym_load_update{{7, 1, nan, 1.0}, {7, 1, 0.5e6, nan}}; + // std::vector mixed_asym_load_update{ + // {8, 1, RealValue{nan}, RealValue{1.0}}, + // {8, 1, RealValue{0.5e6 / 3.0}, RealValue{nan}}}; + + // auto const source_indptr = IdxVector{0, 0, static_cast(mixed_source_update.size())}; + + // REQUIRE(source_indptr.size() == batch_size + 1); + + // ConstDataset mixed_update_data{true, batch_size, "update", meta_data::meta_data_gen::meta_data}; + // mixed_update_data.add_buffer("source", 2, 4, nullptr, mixed_source_update.data()); + // mixed_update_data.add_buffer("sym_load", 1, 2, nullptr, mixed_sym_load_update.data()); + // mixed_update_data.add_buffer("asym_load", 1, 2, nullptr, mixed_asym_load_update.data()); + + // ConstDataset second_scenario_update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; + // second_scenario_update_data.add_buffer("source", 2, 2, nullptr, mixed_source_update.data() + 2); + // second_scenario_update_data.add_buffer("sym_load", 1, 1, nullptr, mixed_sym_load_update.data() + 1); + // second_scenario_update_data.add_buffer("asym_load", 1, 1, nullptr, mixed_asym_load_update.data() + 1); + + // SUBCASE("Symmetrical") { + // MutableDataset test_result_data{true, batch_size, "sym_output", meta_data::meta_data_gen::meta_data}; + // MutableDataset ref_result_data{false, 1, "sym_output", meta_data::meta_data_gen::meta_data}; + + // std::vector> test_sym_node(batch_size * state.sym_node.size(), + // {na_IntID, na_IntS, nan, nan, nan, nan, nan}); + // std::vector> ref_sym_node(state.sym_node.size(), + // {na_IntID, na_IntS, nan, nan, nan, nan, nan}); + // test_result_data.add_buffer("node", state.sym_node.size(), test_sym_node.size(), nullptr, + // test_sym_node.data()); ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), + // nullptr, ref_sym_node.data()); + + // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, + // .calculation_symmetry = symmetric, + // .calculation_method = linear, + // .err_tol = 1e-8, + // .max_iter = 1}, + // test_result_data, mixed_update_data), + // BatchCalculationError); + // model.calculate({.calculation_type = power_flow, + // .calculation_symmetry = symmetric, + // .calculation_method = linear, + // .err_tol = 1e-8, + // .max_iter = 1}, + // ref_result_data, second_scenario_update_data); + + // CHECK(is_nan(test_sym_node[0].u_pu)); + // CHECK(is_nan(test_sym_node[1].u_pu)); + // CHECK(is_nan(test_sym_node[2].u_pu)); + // CHECK(test_sym_node[state.sym_node.size() + 0].u_pu == doctest::Approx(ref_sym_node[0].u_pu)); + // CHECK(test_sym_node[state.sym_node.size() + 1].u_pu == doctest::Approx(ref_sym_node[1].u_pu)); + // CHECK(test_sym_node[state.sym_node.size() + 2].u_pu == doctest::Approx(ref_sym_node[2].u_pu)); + // } + + // SUBCASE("Asymmetrical") { + // MutableDataset test_result_data{true, batch_size, "asym_output", meta_data::meta_data_gen::meta_data}; + // MutableDataset ref_result_data{false, 1, "asym_output", meta_data::meta_data_gen::meta_data}; + + // std::vector> test_asym_node( + // batch_size * state.sym_node.size(), + // {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, + // RealValue{nan}, RealValue{nan}, RealValue{nan}}); + // std::vector> ref_asym_node( + // state.sym_node.size(), + // {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, + // RealValue{nan}, RealValue{nan}, RealValue{nan}}); + // test_result_data.add_buffer("node", state.sym_node.size(), test_asym_node.size(), nullptr, + // test_asym_node.data()); + // ref_result_data.add_buffer("node", ref_asym_node.size(), ref_asym_node.size(), nullptr, + // ref_asym_node.data()); + + // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, + // .calculation_symmetry = asymmetric, + // .calculation_method = linear, + // .err_tol = 1e-8, + // .max_iter = 1}, + // test_result_data, mixed_update_data), + // BatchCalculationError); + // model.calculate({.calculation_type = power_flow, + // .calculation_symmetry = asymmetric, + // .calculation_method = linear, + // .err_tol = 1e-8, + // .max_iter = 1}, + // ref_result_data, second_scenario_update_data); + + // for (auto component_idx : {0, 1, 2}) { + // CAPTURE(component_idx); + + // CHECK(is_nan(test_asym_node[component_idx].u_pu)); + + // for (auto phase_idx : {0, 1, 2}) { + // CAPTURE(phase_idx); + + // CHECK(test_asym_node[state.asym_node.size() + component_idx].u_pu(phase_idx) == + // doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx))); + // } + // } + // } +} } // namespace power_grid_model_cpp From c57e4f6327f29c4ba72c0ee8c909467d40ea3beb Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 15:44:31 +0100 Subject: [PATCH 15/49] fully migrate incomplete input test Signed-off-by: Martijn Govers --- .../native_api_tests/test_api_model_misc.cpp | 162 +++++++++--------- 1 file changed, 77 insertions(+), 85 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index 2a3ac5624..e67d2586a 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -479,10 +479,10 @@ TEST_CASE("API model - incomplete input") { State complete_state; State incomplete_state = get_incomplete_state(); - auto model = Model{50.0, complete_state.get_input_dataset()}; + auto ref_model = Model{50.0, complete_state.get_input_dataset()}; auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; - DatasetConst complete_update_data{"update", false, 1}; + DatasetConst complete_update_data{"update", true, 1}; complete_update_data.add_buffer("source", complete_state.source_id.size(), complete_state.source_id.size(), nullptr, nullptr); complete_update_data.add_attribute_buffer("source", "id", complete_state.source_id.data()); @@ -499,7 +499,7 @@ TEST_CASE("API model - incomplete input") { complete_update_data.add_attribute_buffer("asym_load", "id", complete_state.asym_load_id.data()); complete_update_data.add_attribute_buffer("asym_load", "p_specified", complete_state.asym_load_p_specified.data()); - DatasetConst incomplete_update_data{"update", false, 1}; + DatasetConst incomplete_update_data{"update", true, 1}; incomplete_update_data.add_buffer("source", incomplete_state.source_id.size(), incomplete_state.source_id.size(), nullptr, nullptr); incomplete_update_data.add_attribute_buffer("source", "id", incomplete_state.source_id.data()); @@ -518,89 +518,80 @@ TEST_CASE("API model - incomplete input") { incomplete_update_data.add_attribute_buffer("asym_load", "p_specified", incomplete_state.asym_load_p_specified.data()); - // SUBCASE("Symmetrical - Incomplete") { // TODO(mgovers): not tested elsewhere; maybe test in API model? - // MutableDataset test_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - // MutableDataset const ref_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - - // std::vector> test_sym_node(state.sym_node.size()); - // test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, - // test_sym_node.data()); - - // SUBCASE("Target dataset") { - // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - // .calculation_symmetry = symmetric, - // .calculation_method = linear, - // .err_tol = 1e-8, - // .max_iter = 1}, - // test_result_data), - // SparseMatrixError); - // } - // SUBCASE("Empty update dataset") { - // ConstDataset const update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - - // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - // .calculation_symmetry = symmetric, - // .calculation_method = linear, - // .err_tol = 1e-8, - // .max_iter = 1}, - // test_result_data, update_data), - // SparseMatrixError); - // } - // SUBCASE("Update dataset") { - // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - // .calculation_symmetry = symmetric, - // .calculation_method = linear, - // .err_tol = 1e-8, - // .max_iter = 1}, - // test_result_data, incomplete_update_data), - // BatchCalculationError); - // } - // } - - // SUBCASE("Asymmetrical - Incomplete") { // TODO(mgovers): not tested elsewhere; maybe test in API model? - // MutableDataset test_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; - // MutableDataset const ref_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; - - // std::vector> test_sym_node(state.sym_node.size()); - // test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, - // test_sym_node.data()); - - // SUBCASE("Target dataset") { - // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - // .calculation_symmetry = asymmetric, - // .calculation_method = linear, - // .err_tol = 1e-8, - // .max_iter = 1}, - // test_result_data), - // SparseMatrixError); - // } - // SUBCASE("Empty update dataset") { - // ConstDataset const update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - - // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - // .calculation_symmetry = asymmetric, - // .calculation_method = linear, - // .err_tol = 1e-8, - // .max_iter = 1}, - // test_result_data, update_data), - // SparseMatrixError); - // } - // SUBCASE("Update dataset") { - // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - // .calculation_symmetry = asymmetric, - // .calculation_method = linear, - // .err_tol = 1e-8, - // .max_iter = 1}, - // test_result_data, incomplete_update_data), - // BatchCalculationError); - // } - // } - // } + for (auto symmetry : {PGM_symmetric, PGM_asymmetric}) { + CAPTURE(symmetry); + + auto const calculation_symmetry = symmetry == PGM_symmetric ? "Symmetric" : "Asymmetric"; + auto const output_type = symmetry == PGM_symmetric ? "sym_output" : "asym_output"; + + SUBCASE(calculation_symmetry) { + DatasetMutable test_result_data{output_type, true, 1}; + DatasetMutable ref_result_data{output_type, true, 1}; + + auto n_bytes = complete_state.node_id.size() * + MetaData::component_size(MetaData::get_component_by_name(output_type, "node")); + + std::vector test_sym_node(n_bytes); + std::vector ref_sym_node(n_bytes); + test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, + test_sym_node.data()); + ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), nullptr, ref_sym_node.data()); + + SUBCASE("Target dataset") { + CHECK_THROWS_WITH_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), + doctest::Contains("Sparse matrix error, possibly singular matrix!"), + PowerGridRegularError); + } + SUBCASE("Empty single scenario update dataset") { + DatasetConst const empty_update_data{"update", true, 1}; + SUBCASE("Single update") { + test_model.update(empty_update_data); + CHECK_THROWS_WITH_AS( + test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), + doctest::Contains("Sparse matrix error, possibly singular matrix!"), PowerGridRegularError); + } + SUBCASE("Batch") { + CHECK_THROWS_WITH_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), + test_result_data, empty_update_data), + doctest::Contains("Sparse matrix error, possibly singular matrix!"), + PowerGridRegularError); + } + } + SUBCASE("Incomplete update dataset") { + SUBCASE("Single update") { + CHECK_NOTHROW(test_model.update(incomplete_update_data)); + CHECK_THROWS_WITH_AS( + test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), + doctest::Contains("Sparse matrix error, possibly singular matrix!"), PowerGridRegularError); + } + SUBCASE("Batch") { + CHECK_THROWS_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data, + incomplete_update_data), + PowerGridBatchError); + } + } + SUBCASE("Complete update dataset") { + CHECK_NOTHROW(ref_model.calculate(get_default_options(symmetry, PGM_linear), ref_result_data, + complete_update_data)); + SUBCASE("Single calculation") { + test_model.update(complete_update_data); + CHECK_NOTHROW(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data, + complete_update_data)); + } + SUBCASE("Batch") { + CHECK_NOTHROW(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data, + complete_update_data)); + } + + CHECK(test_sym_node == ref_sym_node); + } + } + } // TEST_CASE("Test main model - Incomplete followed by complete") { // TODO(mgovers): This tests the reset of 2 // consecutive - // // batch scenarios and definitely needs to be - // tested + // // batch scenarios and definitely needs to + // be tested // using CalculationMethod::linear; // State const state; @@ -665,8 +656,9 @@ TEST_CASE("API model - incomplete input") { // } // SUBCASE("Asymmetrical") { - // MutableDataset test_result_data{true, batch_size, "asym_output", meta_data::meta_data_gen::meta_data}; - // MutableDataset ref_result_data{false, 1, "asym_output", meta_data::meta_data_gen::meta_data}; + // MutableDataset test_result_data{true, batch_size, "asym_output", + // meta_data::meta_data_gen::meta_data}; MutableDataset ref_result_data{false, 1, "asym_output", + // meta_data::meta_data_gen::meta_data}; // std::vector> test_asym_node( // batch_size * state.sym_node.size(), From 18514d71944317827f09ad974a1eaf67976142be Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 15:47:26 +0100 Subject: [PATCH 16/49] minor cleanup Signed-off-by: Martijn Govers --- .../native_api_tests/test_api_model_misc.cpp | 318 +++++++++--------- 1 file changed, 162 insertions(+), 156 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index e67d2586a..b3744f01b 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -479,45 +479,8 @@ TEST_CASE("API model - incomplete input") { State complete_state; State incomplete_state = get_incomplete_state(); - auto ref_model = Model{50.0, complete_state.get_input_dataset()}; auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; - DatasetConst complete_update_data{"update", true, 1}; - complete_update_data.add_buffer("source", complete_state.source_id.size(), complete_state.source_id.size(), nullptr, - nullptr); - complete_update_data.add_attribute_buffer("source", "id", complete_state.source_id.data()); - complete_update_data.add_attribute_buffer("source", "u_ref", complete_state.source_u_ref.data()); - complete_update_data.add_attribute_buffer("source", "u_ref_angle", complete_state.source_u_ref_angle.data()); - - complete_update_data.add_buffer("sym_load", complete_state.sym_load_id.size(), complete_state.sym_load_id.size(), - nullptr, nullptr); - complete_update_data.add_attribute_buffer("sym_load", "id", complete_state.sym_load_id.data()); - complete_update_data.add_attribute_buffer("sym_load", "p_specified", complete_state.sym_load_p_specified.data()); - - complete_update_data.add_buffer("asym_load", complete_state.asym_load_id.size(), complete_state.asym_load_id.size(), - nullptr, nullptr); - complete_update_data.add_attribute_buffer("asym_load", "id", complete_state.asym_load_id.data()); - complete_update_data.add_attribute_buffer("asym_load", "p_specified", complete_state.asym_load_p_specified.data()); - - DatasetConst incomplete_update_data{"update", true, 1}; - incomplete_update_data.add_buffer("source", incomplete_state.source_id.size(), incomplete_state.source_id.size(), - nullptr, nullptr); - incomplete_update_data.add_attribute_buffer("source", "id", incomplete_state.source_id.data()); - incomplete_update_data.add_attribute_buffer("source", "u_ref", incomplete_state.source_u_ref.data()); - incomplete_update_data.add_attribute_buffer("source", "u_ref_angle", incomplete_state.source_u_ref_angle.data()); - - incomplete_update_data.add_buffer("sym_load", incomplete_state.sym_load_id.size(), - incomplete_state.sym_load_id.size(), nullptr, nullptr); - incomplete_update_data.add_attribute_buffer("sym_load", "id", incomplete_state.sym_load_id.data()); - incomplete_update_data.add_attribute_buffer("sym_load", "p_specified", - incomplete_state.sym_load_p_specified.data()); - - incomplete_update_data.add_buffer("asym_load", incomplete_state.asym_load_id.size(), - incomplete_state.asym_load_id.size(), nullptr, nullptr); - incomplete_update_data.add_attribute_buffer("asym_load", "id", incomplete_state.asym_load_id.data()); - incomplete_update_data.add_attribute_buffer("asym_load", "p_specified", - incomplete_state.asym_load_p_specified.data()); - for (auto symmetry : {PGM_symmetric, PGM_asymmetric}) { CAPTURE(symmetry); @@ -525,17 +488,14 @@ TEST_CASE("API model - incomplete input") { auto const output_type = symmetry == PGM_symmetric ? "sym_output" : "asym_output"; SUBCASE(calculation_symmetry) { - DatasetMutable test_result_data{output_type, true, 1}; - DatasetMutable ref_result_data{output_type, true, 1}; auto n_bytes = complete_state.node_id.size() * MetaData::component_size(MetaData::get_component_by_name(output_type, "node")); std::vector test_sym_node(n_bytes); - std::vector ref_sym_node(n_bytes); + DatasetMutable test_result_data{output_type, true, 1}; test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, test_sym_node.data()); - ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), nullptr, ref_sym_node.data()); SUBCASE("Target dataset") { CHECK_THROWS_WITH_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), @@ -558,6 +518,26 @@ TEST_CASE("API model - incomplete input") { } } SUBCASE("Incomplete update dataset") { + DatasetConst incomplete_update_data{"update", true, 1}; + incomplete_update_data.add_buffer("source", incomplete_state.source_id.size(), + incomplete_state.source_id.size(), nullptr, nullptr); + incomplete_update_data.add_attribute_buffer("source", "id", incomplete_state.source_id.data()); + incomplete_update_data.add_attribute_buffer("source", "u_ref", incomplete_state.source_u_ref.data()); + incomplete_update_data.add_attribute_buffer("source", "u_ref_angle", + incomplete_state.source_u_ref_angle.data()); + + incomplete_update_data.add_buffer("sym_load", incomplete_state.sym_load_id.size(), + incomplete_state.sym_load_id.size(), nullptr, nullptr); + incomplete_update_data.add_attribute_buffer("sym_load", "id", incomplete_state.sym_load_id.data()); + incomplete_update_data.add_attribute_buffer("sym_load", "p_specified", + incomplete_state.sym_load_p_specified.data()); + + incomplete_update_data.add_buffer("asym_load", incomplete_state.asym_load_id.size(), + incomplete_state.asym_load_id.size(), nullptr, nullptr); + incomplete_update_data.add_attribute_buffer("asym_load", "id", incomplete_state.asym_load_id.data()); + incomplete_update_data.add_attribute_buffer("asym_load", "p_specified", + incomplete_state.asym_load_p_specified.data()); + SUBCASE("Single update") { CHECK_NOTHROW(test_model.update(incomplete_update_data)); CHECK_THROWS_WITH_AS( @@ -571,8 +551,34 @@ TEST_CASE("API model - incomplete input") { } } SUBCASE("Complete update dataset") { - CHECK_NOTHROW(ref_model.calculate(get_default_options(symmetry, PGM_linear), ref_result_data, - complete_update_data)); + DatasetConst complete_update_data{"update", true, 1}; + complete_update_data.add_buffer("source", complete_state.source_id.size(), + complete_state.source_id.size(), nullptr, nullptr); + complete_update_data.add_attribute_buffer("source", "id", complete_state.source_id.data()); + complete_update_data.add_attribute_buffer("source", "u_ref", complete_state.source_u_ref.data()); + complete_update_data.add_attribute_buffer("source", "u_ref_angle", + complete_state.source_u_ref_angle.data()); + + complete_update_data.add_buffer("sym_load", complete_state.sym_load_id.size(), + complete_state.sym_load_id.size(), nullptr, nullptr); + complete_update_data.add_attribute_buffer("sym_load", "id", complete_state.sym_load_id.data()); + complete_update_data.add_attribute_buffer("sym_load", "p_specified", + complete_state.sym_load_p_specified.data()); + + complete_update_data.add_buffer("asym_load", complete_state.asym_load_id.size(), + complete_state.asym_load_id.size(), nullptr, nullptr); + complete_update_data.add_attribute_buffer("asym_load", "id", complete_state.asym_load_id.data()); + complete_update_data.add_attribute_buffer("asym_load", "p_specified", + complete_state.asym_load_p_specified.data()); + + auto ref_model = Model{50.0, complete_state.get_input_dataset()}; + std::vector ref_sym_node(n_bytes); + DatasetMutable ref_result_data{output_type, true, 1}; + ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), nullptr, + ref_sym_node.data()); + + CHECK_NOTHROW(ref_model.calculate(get_default_options(symmetry, PGM_linear), ref_result_data)); + SUBCASE("Single calculation") { test_model.update(complete_update_data); CHECK_NOTHROW(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data, @@ -587,119 +593,119 @@ TEST_CASE("API model - incomplete input") { } } } - - // TEST_CASE("Test main model - Incomplete followed by complete") { // TODO(mgovers): This tests the reset of 2 - // consecutive - // // batch scenarios and definitely needs to - // be tested - // using CalculationMethod::linear; - - // State const state; - // auto model = default_model(state); - // auto test_model = incomplete_input_model(state); - - // constexpr Idx batch_size = 2; - - // std::vector mixed_source_update{ - // {6, 1, nan, nan}, {10, 1, nan, nan}, {6, 1, 1.05, nan}, {10, 1, 1.05, 0}}; - // std::vector mixed_sym_load_update{{7, 1, nan, 1.0}, {7, 1, 0.5e6, nan}}; - // std::vector mixed_asym_load_update{ - // {8, 1, RealValue{nan}, RealValue{1.0}}, - // {8, 1, RealValue{0.5e6 / 3.0}, RealValue{nan}}}; - - // auto const source_indptr = IdxVector{0, 0, static_cast(mixed_source_update.size())}; - - // REQUIRE(source_indptr.size() == batch_size + 1); - - // ConstDataset mixed_update_data{true, batch_size, "update", meta_data::meta_data_gen::meta_data}; - // mixed_update_data.add_buffer("source", 2, 4, nullptr, mixed_source_update.data()); - // mixed_update_data.add_buffer("sym_load", 1, 2, nullptr, mixed_sym_load_update.data()); - // mixed_update_data.add_buffer("asym_load", 1, 2, nullptr, mixed_asym_load_update.data()); - - // ConstDataset second_scenario_update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - // second_scenario_update_data.add_buffer("source", 2, 2, nullptr, mixed_source_update.data() + 2); - // second_scenario_update_data.add_buffer("sym_load", 1, 1, nullptr, mixed_sym_load_update.data() + 1); - // second_scenario_update_data.add_buffer("asym_load", 1, 1, nullptr, mixed_asym_load_update.data() + 1); - - // SUBCASE("Symmetrical") { - // MutableDataset test_result_data{true, batch_size, "sym_output", meta_data::meta_data_gen::meta_data}; - // MutableDataset ref_result_data{false, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - - // std::vector> test_sym_node(batch_size * state.sym_node.size(), - // {na_IntID, na_IntS, nan, nan, nan, nan, nan}); - // std::vector> ref_sym_node(state.sym_node.size(), - // {na_IntID, na_IntS, nan, nan, nan, nan, nan}); - // test_result_data.add_buffer("node", state.sym_node.size(), test_sym_node.size(), nullptr, - // test_sym_node.data()); ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), - // nullptr, ref_sym_node.data()); - - // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - // .calculation_symmetry = symmetric, - // .calculation_method = linear, - // .err_tol = 1e-8, - // .max_iter = 1}, - // test_result_data, mixed_update_data), - // BatchCalculationError); - // model.calculate({.calculation_type = power_flow, - // .calculation_symmetry = symmetric, - // .calculation_method = linear, - // .err_tol = 1e-8, - // .max_iter = 1}, - // ref_result_data, second_scenario_update_data); - - // CHECK(is_nan(test_sym_node[0].u_pu)); - // CHECK(is_nan(test_sym_node[1].u_pu)); - // CHECK(is_nan(test_sym_node[2].u_pu)); - // CHECK(test_sym_node[state.sym_node.size() + 0].u_pu == doctest::Approx(ref_sym_node[0].u_pu)); - // CHECK(test_sym_node[state.sym_node.size() + 1].u_pu == doctest::Approx(ref_sym_node[1].u_pu)); - // CHECK(test_sym_node[state.sym_node.size() + 2].u_pu == doctest::Approx(ref_sym_node[2].u_pu)); - // } - - // SUBCASE("Asymmetrical") { - // MutableDataset test_result_data{true, batch_size, "asym_output", - // meta_data::meta_data_gen::meta_data}; MutableDataset ref_result_data{false, 1, "asym_output", - // meta_data::meta_data_gen::meta_data}; - - // std::vector> test_asym_node( - // batch_size * state.sym_node.size(), - // {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, - // RealValue{nan}, RealValue{nan}, RealValue{nan}}); - // std::vector> ref_asym_node( - // state.sym_node.size(), - // {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, - // RealValue{nan}, RealValue{nan}, RealValue{nan}}); - // test_result_data.add_buffer("node", state.sym_node.size(), test_asym_node.size(), nullptr, - // test_asym_node.data()); - // ref_result_data.add_buffer("node", ref_asym_node.size(), ref_asym_node.size(), nullptr, - // ref_asym_node.data()); - - // CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - // .calculation_symmetry = asymmetric, - // .calculation_method = linear, - // .err_tol = 1e-8, - // .max_iter = 1}, - // test_result_data, mixed_update_data), - // BatchCalculationError); - // model.calculate({.calculation_type = power_flow, - // .calculation_symmetry = asymmetric, - // .calculation_method = linear, - // .err_tol = 1e-8, - // .max_iter = 1}, - // ref_result_data, second_scenario_update_data); - - // for (auto component_idx : {0, 1, 2}) { - // CAPTURE(component_idx); - - // CHECK(is_nan(test_asym_node[component_idx].u_pu)); - - // for (auto phase_idx : {0, 1, 2}) { - // CAPTURE(phase_idx); - - // CHECK(test_asym_node[state.asym_node.size() + component_idx].u_pu(phase_idx) == - // doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx))); - // } - // } - // } } +// TEST_CASE("Test main model - Incomplete followed by complete") { // TODO(mgovers): This tests the reset of 2 +// consecutive +// // batch scenarios and definitely needs to +// be tested +// using CalculationMethod::linear; + +// State const state; +// auto model = default_model(state); +// auto test_model = incomplete_input_model(state); + +// constexpr Idx batch_size = 2; + +// std::vector mixed_source_update{ +// {6, 1, nan, nan}, {10, 1, nan, nan}, {6, 1, 1.05, nan}, {10, 1, 1.05, 0}}; +// std::vector mixed_sym_load_update{{7, 1, nan, 1.0}, {7, 1, 0.5e6, nan}}; +// std::vector mixed_asym_load_update{ +// {8, 1, RealValue{nan}, RealValue{1.0}}, +// {8, 1, RealValue{0.5e6 / 3.0}, RealValue{nan}}}; + +// auto const source_indptr = IdxVector{0, 0, static_cast(mixed_source_update.size())}; + +// REQUIRE(source_indptr.size() == batch_size + 1); + +// ConstDataset mixed_update_data{true, batch_size, "update", meta_data::meta_data_gen::meta_data}; +// mixed_update_data.add_buffer("source", 2, 4, nullptr, mixed_source_update.data()); +// mixed_update_data.add_buffer("sym_load", 1, 2, nullptr, mixed_sym_load_update.data()); +// mixed_update_data.add_buffer("asym_load", 1, 2, nullptr, mixed_asym_load_update.data()); + +// ConstDataset second_scenario_update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; +// second_scenario_update_data.add_buffer("source", 2, 2, nullptr, mixed_source_update.data() + 2); +// second_scenario_update_data.add_buffer("sym_load", 1, 1, nullptr, mixed_sym_load_update.data() + 1); +// second_scenario_update_data.add_buffer("asym_load", 1, 1, nullptr, mixed_asym_load_update.data() + 1); + +// SUBCASE("Symmetrical") { +// MutableDataset test_result_data{true, batch_size, "sym_output", meta_data::meta_data_gen::meta_data}; +// MutableDataset ref_result_data{false, 1, "sym_output", meta_data::meta_data_gen::meta_data}; + +// std::vector> test_sym_node(batch_size * state.sym_node.size(), +// {na_IntID, na_IntS, nan, nan, nan, nan, nan}); +// std::vector> ref_sym_node(state.sym_node.size(), +// {na_IntID, na_IntS, nan, nan, nan, nan, nan}); +// test_result_data.add_buffer("node", state.sym_node.size(), test_sym_node.size(), nullptr, +// test_sym_node.data()); ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), +// nullptr, ref_sym_node.data()); + +// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = symmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// test_result_data, mixed_update_data), +// BatchCalculationError); +// model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = symmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// ref_result_data, second_scenario_update_data); + +// CHECK(is_nan(test_sym_node[0].u_pu)); +// CHECK(is_nan(test_sym_node[1].u_pu)); +// CHECK(is_nan(test_sym_node[2].u_pu)); +// CHECK(test_sym_node[state.sym_node.size() + 0].u_pu == doctest::Approx(ref_sym_node[0].u_pu)); +// CHECK(test_sym_node[state.sym_node.size() + 1].u_pu == doctest::Approx(ref_sym_node[1].u_pu)); +// CHECK(test_sym_node[state.sym_node.size() + 2].u_pu == doctest::Approx(ref_sym_node[2].u_pu)); +// } + +// SUBCASE("Asymmetrical") { +// MutableDataset test_result_data{true, batch_size, "asym_output", +// meta_data::meta_data_gen::meta_data}; MutableDataset ref_result_data{false, 1, "asym_output", +// meta_data::meta_data_gen::meta_data}; + +// std::vector> test_asym_node( +// batch_size * state.sym_node.size(), +// {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, +// RealValue{nan}, RealValue{nan}, RealValue{nan}}); +// std::vector> ref_asym_node( +// state.sym_node.size(), +// {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, +// RealValue{nan}, RealValue{nan}, RealValue{nan}}); +// test_result_data.add_buffer("node", state.sym_node.size(), test_asym_node.size(), nullptr, +// test_asym_node.data()); +// ref_result_data.add_buffer("node", ref_asym_node.size(), ref_asym_node.size(), nullptr, +// ref_asym_node.data()); + +// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = asymmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// test_result_data, mixed_update_data), +// BatchCalculationError); +// model.calculate({.calculation_type = power_flow, +// .calculation_symmetry = asymmetric, +// .calculation_method = linear, +// .err_tol = 1e-8, +// .max_iter = 1}, +// ref_result_data, second_scenario_update_data); + +// for (auto component_idx : {0, 1, 2}) { +// CAPTURE(component_idx); + +// CHECK(is_nan(test_asym_node[component_idx].u_pu)); + +// for (auto phase_idx : {0, 1, 2}) { +// CAPTURE(phase_idx); + +// CHECK(test_asym_node[state.asym_node.size() + component_idx].u_pu(phase_idx) == +// doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx))); +// } +// } +// } + } // namespace power_grid_model_cpp From e88c4b5c2308d37cc8bc1190b817cdcb5ca4de4b Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 16:33:31 +0100 Subject: [PATCH 17/49] migrate final test Signed-off-by: Martijn Govers --- .../native_api_tests/test_api_model_misc.cpp | 235 +++++++++--------- 1 file changed, 118 insertions(+), 117 deletions(-) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp index b3744f01b..a46eb7156 100644 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ b/tests/native_api_tests/test_api_model_misc.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -20,7 +21,6 @@ using std::numbers::pi; using std::numbers::sqrt3; constexpr Idx default_option{-1}; -constexpr double nan = std::numeric_limits::quiet_NaN(); constexpr double deg_120 = 2.0 / 3.0 * pi; constexpr double deg_240 = 4.0 / 3.0 * pi; @@ -332,7 +332,6 @@ TEST_CASE("API model - all updates") { TEST_CASE("API model - updates w/ alternating compute mode") { State state; auto const input_dataset = state.get_input_dataset(); - auto const& input_info = input_dataset.get_info(); auto model = Model{50.0, input_dataset}; auto const check_sym = [&] { @@ -476,8 +475,8 @@ auto get_incomplete_state() -> State { } // namespace TEST_CASE("API model - incomplete input") { - State complete_state; - State incomplete_state = get_incomplete_state(); + State const complete_state; + State const incomplete_state = get_incomplete_state(); auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; @@ -488,7 +487,6 @@ TEST_CASE("API model - incomplete input") { auto const output_type = symmetry == PGM_symmetric ? "sym_output" : "asym_output"; SUBCASE(calculation_symmetry) { - auto n_bytes = complete_state.node_id.size() * MetaData::component_size(MetaData::get_component_by_name(output_type, "node")); @@ -595,117 +593,120 @@ TEST_CASE("API model - incomplete input") { } } -// TEST_CASE("Test main model - Incomplete followed by complete") { // TODO(mgovers): This tests the reset of 2 -// consecutive -// // batch scenarios and definitely needs to -// be tested -// using CalculationMethod::linear; - -// State const state; -// auto model = default_model(state); -// auto test_model = incomplete_input_model(state); - -// constexpr Idx batch_size = 2; - -// std::vector mixed_source_update{ -// {6, 1, nan, nan}, {10, 1, nan, nan}, {6, 1, 1.05, nan}, {10, 1, 1.05, 0}}; -// std::vector mixed_sym_load_update{{7, 1, nan, 1.0}, {7, 1, 0.5e6, nan}}; -// std::vector mixed_asym_load_update{ -// {8, 1, RealValue{nan}, RealValue{1.0}}, -// {8, 1, RealValue{0.5e6 / 3.0}, RealValue{nan}}}; - -// auto const source_indptr = IdxVector{0, 0, static_cast(mixed_source_update.size())}; - -// REQUIRE(source_indptr.size() == batch_size + 1); - -// ConstDataset mixed_update_data{true, batch_size, "update", meta_data::meta_data_gen::meta_data}; -// mixed_update_data.add_buffer("source", 2, 4, nullptr, mixed_source_update.data()); -// mixed_update_data.add_buffer("sym_load", 1, 2, nullptr, mixed_sym_load_update.data()); -// mixed_update_data.add_buffer("asym_load", 1, 2, nullptr, mixed_asym_load_update.data()); - -// ConstDataset second_scenario_update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; -// second_scenario_update_data.add_buffer("source", 2, 2, nullptr, mixed_source_update.data() + 2); -// second_scenario_update_data.add_buffer("sym_load", 1, 1, nullptr, mixed_sym_load_update.data() + 1); -// second_scenario_update_data.add_buffer("asym_load", 1, 1, nullptr, mixed_asym_load_update.data() + 1); - -// SUBCASE("Symmetrical") { -// MutableDataset test_result_data{true, batch_size, "sym_output", meta_data::meta_data_gen::meta_data}; -// MutableDataset ref_result_data{false, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - -// std::vector> test_sym_node(batch_size * state.sym_node.size(), -// {na_IntID, na_IntS, nan, nan, nan, nan, nan}); -// std::vector> ref_sym_node(state.sym_node.size(), -// {na_IntID, na_IntS, nan, nan, nan, nan, nan}); -// test_result_data.add_buffer("node", state.sym_node.size(), test_sym_node.size(), nullptr, -// test_sym_node.data()); ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), -// nullptr, ref_sym_node.data()); - -// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = symmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// test_result_data, mixed_update_data), -// BatchCalculationError); -// model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = symmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// ref_result_data, second_scenario_update_data); - -// CHECK(is_nan(test_sym_node[0].u_pu)); -// CHECK(is_nan(test_sym_node[1].u_pu)); -// CHECK(is_nan(test_sym_node[2].u_pu)); -// CHECK(test_sym_node[state.sym_node.size() + 0].u_pu == doctest::Approx(ref_sym_node[0].u_pu)); -// CHECK(test_sym_node[state.sym_node.size() + 1].u_pu == doctest::Approx(ref_sym_node[1].u_pu)); -// CHECK(test_sym_node[state.sym_node.size() + 2].u_pu == doctest::Approx(ref_sym_node[2].u_pu)); -// } - -// SUBCASE("Asymmetrical") { -// MutableDataset test_result_data{true, batch_size, "asym_output", -// meta_data::meta_data_gen::meta_data}; MutableDataset ref_result_data{false, 1, "asym_output", -// meta_data::meta_data_gen::meta_data}; - -// std::vector> test_asym_node( -// batch_size * state.sym_node.size(), -// {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, -// RealValue{nan}, RealValue{nan}, RealValue{nan}}); -// std::vector> ref_asym_node( -// state.sym_node.size(), -// {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, -// RealValue{nan}, RealValue{nan}, RealValue{nan}}); -// test_result_data.add_buffer("node", state.sym_node.size(), test_asym_node.size(), nullptr, -// test_asym_node.data()); -// ref_result_data.add_buffer("node", ref_asym_node.size(), ref_asym_node.size(), nullptr, -// ref_asym_node.data()); - -// CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = asymmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// test_result_data, mixed_update_data), -// BatchCalculationError); -// model.calculate({.calculation_type = power_flow, -// .calculation_symmetry = asymmetric, -// .calculation_method = linear, -// .err_tol = 1e-8, -// .max_iter = 1}, -// ref_result_data, second_scenario_update_data); - -// for (auto component_idx : {0, 1, 2}) { -// CAPTURE(component_idx); - -// CHECK(is_nan(test_asym_node[component_idx].u_pu)); - -// for (auto phase_idx : {0, 1, 2}) { -// CAPTURE(phase_idx); - -// CHECK(test_asym_node[state.asym_node.size() + component_idx].u_pu(phase_idx) == -// doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx))); -// } -// } -// } +TEST_CASE("API model - Incomplete scenario update followed by complete") { + State const complete_state; + State const incomplete_state = get_incomplete_state(); + + auto ref_model = Model{50.0, complete_state.get_input_dataset()}; + auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; + + constexpr Idx batch_size = 2; + auto const n_nodes = complete_state.node_id.size(); + + std::vector mixed_source_update_id{6, 10, 6, 10}; + std::vector mixed_source_update_status{1, 1, 1, 1}; + std::vector mixed_source_update_u_ref{nan, nan, 1.05, 1.05}; + std::vector mixed_source_update_u_ref_angle{nan, nan, nan, 0}; + + std::vector mixed_sym_load_update_id{7, 7}; + std::vector mixed_sym_load_update_status{1, 1}; + std::vector mixed_sym_load_update_p_specified{nan, 0.5e6}; + std::vector mixed_sym_load_update_q_specified{1.0, nan}; + + std::vector mixed_asym_load_update_id{8, 8}; + std::vector mixed_asym_load_update_status{1, 1}; + std::vector mixed_asym_load_update_p_specified{nan, nan, nan, 0.5e6 / 3.0, 0.5e6 / 3.0, 0.5e6 / 3.0}; + std::vector mixed_asym_load_update_q_specified{1.0, 1.0, 1.0, nan, nan, nan}; + + std::vector const source_indptr{0, 0, static_cast(mixed_source_update_id.size())}; + + REQUIRE(source_indptr.size() == batch_size + 1); + + DatasetConst mixed_update_data{"update", true, batch_size}; + + mixed_update_data.add_buffer("source", 2, 4, nullptr, nullptr); + mixed_update_data.add_attribute_buffer("source", "id", mixed_source_update_id.data()); + mixed_update_data.add_attribute_buffer("source", "status", mixed_source_update_status.data()); + mixed_update_data.add_attribute_buffer("source", "u_ref", mixed_source_update_u_ref.data()); + mixed_update_data.add_attribute_buffer("source", "u_ref_angle", mixed_source_update_u_ref_angle.data()); + + mixed_update_data.add_buffer("sym_load", 1, 2, nullptr, nullptr); + mixed_update_data.add_attribute_buffer("sym_load", "id", mixed_sym_load_update_id.data()); + mixed_update_data.add_attribute_buffer("sym_load", "status", mixed_sym_load_update_status.data()); + mixed_update_data.add_attribute_buffer("sym_load", "p_specified", mixed_sym_load_update_p_specified.data()); + mixed_update_data.add_attribute_buffer("sym_load", "q_specified", mixed_sym_load_update_q_specified.data()); + + mixed_update_data.add_buffer("asym_load", 1, 2, nullptr, nullptr); + mixed_update_data.add_attribute_buffer("asym_load", "id", mixed_asym_load_update_id.data()); + mixed_update_data.add_attribute_buffer("asym_load", "status", mixed_asym_load_update_status.data()); + mixed_update_data.add_attribute_buffer("asym_load", "p_specified", mixed_asym_load_update_p_specified.data()); + mixed_update_data.add_attribute_buffer("asym_load", "q_specified", mixed_asym_load_update_q_specified.data()); + + DatasetConst second_scenario_update_data{"update", true, 1}; + + second_scenario_update_data.add_buffer("source", 2, 2, nullptr, nullptr); + second_scenario_update_data.add_attribute_buffer("source", "id", mixed_source_update_id.data() + 2); + second_scenario_update_data.add_attribute_buffer("source", "status", mixed_source_update_status.data() + 2); + second_scenario_update_data.add_attribute_buffer("source", "u_ref", mixed_source_update_u_ref.data() + 2); + second_scenario_update_data.add_attribute_buffer("source", "u_ref_angle", + mixed_source_update_u_ref_angle.data() + 2); + + second_scenario_update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); + second_scenario_update_data.add_attribute_buffer("sym_load", "id", mixed_sym_load_update_id.data() + 1); + second_scenario_update_data.add_attribute_buffer("sym_load", "status", mixed_sym_load_update_status.data() + 1); + second_scenario_update_data.add_attribute_buffer("sym_load", "p_specified", + mixed_sym_load_update_p_specified.data() + 1); + second_scenario_update_data.add_attribute_buffer("sym_load", "q_specified", + mixed_sym_load_update_q_specified.data() + 1); + + second_scenario_update_data.add_buffer("asym_load", 1, 1, nullptr, nullptr); + second_scenario_update_data.add_attribute_buffer("asym_load", "id", mixed_asym_load_update_id.data() + 1); + second_scenario_update_data.add_attribute_buffer("asym_load", "status", mixed_asym_load_update_status.data() + 1); + second_scenario_update_data.add_attribute_buffer("asym_load", "p_specified", + mixed_asym_load_update_p_specified.data() + 1); + second_scenario_update_data.add_attribute_buffer("asym_load", "q_specified", + mixed_asym_load_update_q_specified.data() + 1); + + for (auto symmetry : {PGM_symmetric, PGM_asymmetric}) { + CAPTURE(symmetry); + + auto const calculation_symmetry = symmetry == PGM_symmetric ? "Symmetric" : "Asymmetric"; + auto const output_type = symmetry == PGM_symmetric ? "sym_output" : "asym_output"; + auto const n_phases = symmetry == PGM_symmetric ? 1 : 3; + + SUBCASE(calculation_symmetry) { + DatasetMutable test_result_data{output_type, true, batch_size}; + DatasetMutable ref_result_data{output_type, true, 1}; + + std::vector test_sym_node_u_pu(batch_size * n_nodes * n_phases, nan); + std::vector ref_sym_node_u_pu(n_nodes * n_phases, nan); + + test_result_data.add_buffer("node", n_nodes, batch_size * n_nodes, nullptr, nullptr); + test_result_data.add_attribute_buffer("node", "u_pu", test_sym_node_u_pu.data()); + + ref_result_data.add_buffer("node", n_nodes, n_nodes, nullptr, nullptr); + ref_result_data.add_attribute_buffer("node", "u_pu", ref_sym_node_u_pu.data()); + + CHECK_THROWS_AS(test_model.calculate(get_default_options(PGM_symmetric, PGM_linear), test_result_data, + mixed_update_data), + PowerGridBatchError); + + ref_model.calculate(get_default_options(PGM_symmetric, PGM_linear), ref_result_data, + second_scenario_update_data); + + for (auto node_idx = 0; node_idx < n_nodes; ++node_idx) { + CAPTURE(node_idx); + + for (auto phase_idx = 0; phase_idx < n_phases; ++phase_idx) { + CAPTURE(phase_idx); + + CHECK(is_nan(test_sym_node_u_pu[node_idx * n_phases + phase_idx])); + CHECK(test_sym_node_u_pu[(n_nodes + node_idx) * n_phases + phase_idx] == + doctest::Approx(ref_sym_node_u_pu[node_idx * n_phases + phase_idx])); + } + } + } + } +} } // namespace power_grid_model_cpp From 84b60ba6f7160eedd26fb2b10bfdfbbc8b2c9ef8 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 16:34:40 +0100 Subject: [PATCH 18/49] finally remove last main model integration tests Signed-off-by: Martijn Govers --- tests/CMakeLists.txt | 1 - tests/cpp_integration_tests/CMakeLists.txt | 19 - .../test_entry_point.cpp | 9 - .../cpp_integration_tests/test_main_model.cpp | 929 ------------------ 4 files changed, 958 deletions(-) delete mode 100644 tests/cpp_integration_tests/CMakeLists.txt delete mode 100644 tests/cpp_integration_tests/test_entry_point.cpp delete mode 100644 tests/cpp_integration_tests/test_main_model.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b39378df3..6e983b5a0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,5 @@ include("${doctest_DIR}/doctest.cmake") add_subdirectory("native_api_tests") add_subdirectory("cpp_unit_tests") -add_subdirectory("cpp_integration_tests") add_subdirectory("cpp_validation_tests") add_subdirectory("benchmark_cpp") diff --git a/tests/cpp_integration_tests/CMakeLists.txt b/tests/cpp_integration_tests/CMakeLists.txt deleted file mode 100644 index f8a4827f6..000000000 --- a/tests/cpp_integration_tests/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-FileCopyrightText: Contributors to the Power Grid Model project -# -# SPDX-License-Identifier: MPL-2.0 - -set(PROJECT_SOURCES - "test_entry_point.cpp" - "test_main_model.cpp" -) - -add_executable(power_grid_model_integration_tests ${PROJECT_SOURCES}) - -target_link_libraries(power_grid_model_integration_tests - PRIVATE - power_grid_model - doctest::doctest - nlohmann_json nlohmann_json::nlohmann_json -) - -doctest_discover_tests(power_grid_model_integration_tests) diff --git a/tests/cpp_integration_tests/test_entry_point.cpp b/tests/cpp_integration_tests/test_entry_point.cpp deleted file mode 100644 index 020ee88a4..000000000 --- a/tests/cpp_integration_tests/test_entry_point.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-FileCopyrightText: Contributors to the Power Grid Model project -// -// SPDX-License-Identifier: MPL-2.0 - -// main cpp file - -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN - -#include diff --git a/tests/cpp_integration_tests/test_main_model.cpp b/tests/cpp_integration_tests/test_main_model.cpp deleted file mode 100644 index 6d73158ef..000000000 --- a/tests/cpp_integration_tests/test_main_model.cpp +++ /dev/null @@ -1,929 +0,0 @@ -// SPDX-FileCopyrightText: Contributors to the Power Grid Model project -// -// SPDX-License-Identifier: MPL-2.0 - -#include -#include - -#include - -namespace power_grid_model { -namespace { -using CalculationType::power_flow; -using enum CalculationSymmetry; - -constexpr auto get_default_options(CalculationSymmetry calculation_symmetry, CalculationMethod calculation_method, - Idx threading = -1) { - return MainModel::Options{.calculation_type = power_flow, - .calculation_symmetry = calculation_symmetry, - .calculation_method = calculation_method, - .err_tol = 1e-8, - .max_iter = 20, - .threading = threading}; -} - -struct regular_update { - using update_type = permanent_update_t; -}; - -struct cached_update { - using update_type = cached_update_t; -}; - -namespace test { -constexpr double z_bus_2 = 1.0 / (0.015 + 0.5e6 / 10e3 / 10e3 * 2); -constexpr double z_total = z_bus_2 + 10.0; -constexpr double u1 = 1.05 * z_bus_2 / (z_bus_2 + 10.0); -constexpr double i = 1.05 * 10e3 / z_total / sqrt3; -constexpr double i_shunt = 0.015 / 0.025 * i; -constexpr double i_load = 0.005 / 0.025 * i; -} // namespace test - -struct State { - // TODO(mgovers): values identical to power_flow/dummy-test/input.json validation case - std::vector node_input{{1, 10e3}, {2, 10e3}, {3, 10e3}}; - std::vector line_input{{4, 1, 2, 1, 1, 10.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 1e3}}; - std::vector link_input{{5, 2, 3, 1, 1}}; - std::vector source_input{{6, 1, 1, 1.05, nan, 1e12, nan, nan}, {10, 3, 0, 1.05, 0.0, 1e12, nan, nan}}; - std::vector sym_load_input{{7, 3, 1, LoadGenType::const_y, 0.5e6, 0.0}}; - std::vector asym_load_input{ - {8, 3, 1, LoadGenType::const_y, RealValue{0.5e6 / 3.0}, RealValue{0.0}}}; - std::vector shunt_input{{9, 3, 1, 0.015, 0.0, 0.015, 0.0}}; - - // TODO(mgovers): only power_flow/pandapower pf validation cases have sensor input and they differ from here - // {{{id}, measured_object}, measured_terminal_type, power_sigma, p_measured, q_measured} - std::vector sym_power_sensor_input{ - {11, 4, MeasuredTerminalType::branch_from, 0.02, 1.1e6, 1.1e3, nan, nan}, - {13, 6, MeasuredTerminalType::source, 0.02, 1.3e6, 1.3e3, nan, nan}, - {14, 6, MeasuredTerminalType::source, 0.02, 1.4e6, 1.4e3, nan, nan}, - {15, 9, MeasuredTerminalType::shunt, 0.02, 1.5e6, 1.5e3, nan, nan}, - {16, 7, MeasuredTerminalType::load, 0.02, 1.6e6, 1.6e3, nan, nan}, - {17, 8, MeasuredTerminalType::load, 0.02, 1.7e6, 1.7e3, nan, nan}, - {28, 3, MeasuredTerminalType::node, 0.02, 3.0e6, 3.0e3, nan, nan}}; - - // TODO(mgovers): only power_flow/pandapower pf validation cases have sensor input and they differ from here - // {{{id}, measured_object}, measured_terminal_type, power_sigma, p_measured, q_measured} - std::vector asym_power_sensor_input{{18, - 4, - MeasuredTerminalType::branch_from, - 0.02, - {2.11e6, 2.12e6, 2.13e6}, - {2.11e3, 2.12e3, 2.13e3}, - {nan, nan, nan}, - {nan, nan, nan}}, - {20, - 6, - MeasuredTerminalType::source, - 0.02, - {2.31e6, 2.32e6, 2.33e6}, - {2.31e3, 2.32e3, 2.33e3}, - {nan, nan, nan}, - {nan, nan, nan}}, - {21, - 6, - MeasuredTerminalType::source, - 0.02, - {2.41e6, 2.42e6, 2.43e6}, - {2.41e3, 2.42e3, 2.43e3}, - {nan, nan, nan}, - {nan, nan, nan}}, - {22, - 9, - MeasuredTerminalType::shunt, - 0.02, - {2.51e6, 2.52e6, 2.53e6}, - {2.51e3, 2.52e3, 2.53e3}, - {nan, nan, nan}, - {nan, nan, nan}}, - {23, - 7, - MeasuredTerminalType::load, - 0.02, - {2.61e6, 2.62e6, 2.63e6}, - {2.61e3, 2.62e3, 2.63e3}, - {nan, nan, nan}, - {nan, nan, nan}}, - {24, - 8, - MeasuredTerminalType::load, - 0.02, - {2.71e6, 2.72e6, 2.73e6}, - {2.71e3, 2.72e3, 2.73e3}, - {nan, nan, nan}, - {nan, nan, nan}}, - {29, - 3, - MeasuredTerminalType::node, - 0.02, - {5.01e6, 5.02e6, 5.03e6}, - {5.01e3, 5.02e3, 5.03e3}, - {nan, nan, nan}, - {nan, nan, nan}}}; - - // TODO(mgovers): only power_flow/pandapower pf validation cases have sensor input and they differ from here - // {{{id}, measured_object}, u_sigma, u_measured, u_angle_measured} - std::vector sym_voltage_sensor_input{{25, 1, 105.0, 10.1e3, 0.1}, - {26, 2, 105.0, 10.2e3, 0.2}}; - - // TODO(mgovers): only power_flow/pandapower pf validation cases have sensor input and they differ from here - // {{{id}, measured_object}, u_sigma, u_measured, u_angle_measured} - std::vector asym_voltage_sensor_input{ - {27, 3, 105.0, {10.31e3 / sqrt3, 10.32e3 / sqrt3, 10.33e3 / sqrt3}, {0.0, -deg_120, -deg_240}}}; - - // TODO(mgovers): only used for updating (and proving no different output). nowhere else used in powerflow - // TODO(mgovers): no powerflow validation cases have fault input - std::vector fault_input{{30, 1, FaultType::single_phase_to_ground, FaultPhase::a, 3, 0.1, 0.1}}; - - // output vector - std::vector> sym_node = std::vector>(3); - std::vector> sym_branch = std::vector>(2); - std::vector> sym_appliance = std::vector>(5); - std::vector> asym_node = std::vector>(3); - std::vector> asym_branch = std::vector>(2); - std::vector> asym_appliance = std::vector>(5); - - // individual symmetric - std::vector> sym_line = std::vector>(1); - std::vector> sym_link = std::vector>(1); - std::vector> sym_load_sym = std::vector>(1); - std::vector> sym_load_asym = std::vector>(1); - std::vector> sym_source = std::vector>(2); - std::vector> sym_shunt = std::vector>(1); - std::vector> sym_voltage_sensor = std::vector>(2); - std::vector> asym_voltage_sensor_sym_output = - std::vector>(1); - std::vector> sym_power_sensor = std::vector>(7); - std::vector> asym_power_sensor_sym_output = - std::vector>(7); - - // individual asymmetric - std::vector> asym_line = std::vector>(1); - std::vector> asym_link = std::vector>(1); - std::vector> asym_load_sym = std::vector>(1); - std::vector> asym_load_asym = std::vector>(1); - std::vector> asym_source = std::vector>(2); - std::vector> asym_shunt = std::vector>(1); - std::vector> asym_voltage_sensor = - std::vector>(1); - std::vector> sym_voltage_sensor_asym_output = - std::vector>(2); - std::vector> asym_power_sensor = std::vector>(7); - std::vector> sym_power_sensor_asym_output = - std::vector>(7); - - // update vector - std::vector sym_load_update{{7, 1, 1.0e6, nan}}; - std::vector asym_load_update{{8, 0, RealValue{nan}, RealValue{nan}}}; - std::vector shunt_update{{9, 0, nan, 0.02, nan, 0.02}}; - std::vector shunt_update_2{{6, 0, nan, 0.01, nan, 0.01}}; // used for test case alternate compute mode - std::vector source_update{{10, 1, test::u1, nan}}; - std::vector link_update{{5, 1, 0}}; - std::vector fault_update{{30, 1, FaultType::three_phase, FaultPhase::abc, 1, nan, nan}}; - - // batch update vector - std::vector batch_sym_load_update{{7, 1, 1.0e6, nan}, {7}, {7}, {7}, {7}}; - std::vector batch_asym_load_update{ - {8, 0, RealValue{nan}, RealValue{nan}}, {8}, {8}, {8}, {8}}; - std::vector batch_shunt_update{{9, 0, nan, 0.02, nan, 0.02}, {9}, {9}, {9}, {9}}; - std::vector batch_source_update{{10, 1, test::u1, nan}, {10}, {10}, {10}, {10}}; - std::vector batch_link_update{{5, 1, 0}, {5}, {5}, {5}, {5}}; - std::vector batch_fault_update{ - {30, 1, FaultType::three_phase, FaultPhase::abc, 1, nan, nan}, {30}, {30}, {30}, {30}}; -}; - -auto default_model(State const& state) -> MainModel { - MainModel main_model{50.0, meta_data::meta_data_gen::meta_data}; - main_model.add_component(state.node_input); - main_model.add_component(state.line_input); - main_model.add_component(state.link_input); - main_model.add_component(state.source_input); - main_model.add_component(state.asym_load_input); - main_model.add_component(state.sym_load_input); - main_model.add_component(state.shunt_input); - main_model.add_component(state.sym_power_sensor_input); - main_model.add_component(state.asym_power_sensor_input); - main_model.add_component(state.sym_voltage_sensor_input); - main_model.add_component(state.asym_voltage_sensor_input); - main_model.add_component(state.fault_input); - main_model.set_construction_complete(); - return main_model; -} -} // namespace - -TEST_CASE("Test main model - power flow") { - State state; - auto main_model = default_model(state); - - SUBCASE("Test get indexer") { // TODO(mgovers): needed - std::vector const node_id{2, 1, 3, 2}; - IdxVector const expected_indexer{1, 0, 2, 1}; - IdxVector indexer(4); - main_model.get_indexer("node", node_id.data(), 4, indexer.data()); - CHECK(indexer == expected_indexer); - } - - SUBCASE("Test duplicated id") { // TODO(mgovers): needed; captured in Python test; maybe move to - // test_main_core_input.cpp - MainModel main_model2{50.0, meta_data::meta_data_gen::meta_data}; - state.node_input[1].id = 1; - CHECK_THROWS_AS(main_model2.add_component(state.node_input), ConflictID); - } - - SUBCASE("Test no existing id") { // TODO(mgovers): needed; captured in Python test; maybe move to - // test_main_core_input.cpp - MainModel main_model2{50.0, meta_data::meta_data_gen::meta_data}; - state.line_input[0].from_node = 100; - main_model2.add_component(state.node_input); - CHECK_THROWS_AS(main_model2.add_component(state.line_input), IDNotFound); - } - - SUBCASE("Test id for wrong type") { // TODO(mgovers): needed; captured in Python test but not all flavors; maybe - // move to test_main_core_input.cpp - MainModel main_model2{50.0, meta_data::meta_data_gen::meta_data}; - - state.link_input[0].from_node = 4; - main_model2.add_component(state.node_input); // 1 2 3 - main_model2.add_component(state.line_input); // 4 - CHECK_THROWS_AS(main_model2.add_component(state.link_input), IDWrongType); - - // Fix link input, retry - state.link_input[0].from_node = 2; - main_model2.add_component(state.link_input); // 5 - - main_model2.add_component(state.source_input); // 6 10 - main_model2.add_component(state.sym_load_input); // 7 - main_model2.add_component(state.asym_load_input); // 8 - main_model2.add_component(state.shunt_input); // 9 - - // voltage sensor with a measured id which is not a node (link) - state.sym_voltage_sensor_input[0].measured_object = 5; - CHECK_THROWS_AS(main_model2.add_component(state.sym_voltage_sensor_input), IDWrongType); - - // Test for all MeasuredTerminalType instances - using enum MeasuredTerminalType; - std::vector const mt_types{branch_from, branch_to, generator, load, shunt, source}; - - // power sensor with terminal branch, with a measured id which is not a branch (node) - for (auto const& mt_type : mt_types) { - state.sym_power_sensor_input[0].measured_object = 1; - state.sym_power_sensor_input[0].measured_terminal_type = mt_type; - CHECK_THROWS_AS(main_model2.add_component(state.sym_power_sensor_input), IDWrongType); - } - } -} - -TEST_CASE_TEMPLATE("Test main model - unknown id", settings, regular_update, - cached_update) { // TODO(mgovers): we need this test - State const state; - auto main_model = default_model(state); - - std::vector const source_update2{SourceUpdate{100, true, nan, nan}}; - ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data.add_buffer("source", source_update2.size(), source_update2.size(), nullptr, source_update2.data()); - CHECK_THROWS_AS((main_model.update_components(update_data)), IDNotFound); -} - -TEST_CASE_TEMPLATE( - "Test main model - update only load", settings, regular_update, - cached_update) { // TODO(mgovers): we should whitebox-test this instead; values not reproduced by validation tests - State state; - auto main_model = default_model(state); - - ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, - state.sym_load_update.data()); - update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, - state.asym_load_update.data()); - main_model.update_components(update_data); - - SUBCASE("Symmetrical") { - auto const solver_output = - main_model.calculate(get_default_options(symmetric, CalculationMethod::linear)); - main_model.output_result(solver_output, state.sym_node); - main_model.output_result(solver_output, state.sym_branch); - main_model.output_result(solver_output, state.sym_appliance); - CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[1].u_pu == doctest::Approx(test::u1)); - CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); - CHECK(state.sym_branch[0].i_from == doctest::Approx(test::i)); - CHECK(state.sym_appliance[0].i == doctest::Approx(test::i)); - CHECK(state.sym_appliance[1].i == doctest::Approx(0.0)); - CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load * 2)); - CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); - CHECK(state.sym_appliance[4].i == doctest::Approx(test::i_shunt)); - } - SUBCASE("Asymmetrical") { - auto const solver_output = main_model.calculate( - get_default_options(asymmetric, CalculationMethod::linear)); - main_model.output_result(solver_output, state.asym_node); - main_model.output_result(solver_output, state.asym_branch); - main_model.output_result(solver_output, state.asym_appliance); - CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); - CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); - CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); - CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(test::i)); - CHECK(state.asym_appliance[0].i(1) == doctest::Approx(test::i)); - CHECK(state.asym_appliance[1].i(2) == doctest::Approx(0.0)); - CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2)); - CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); - CHECK(state.asym_appliance[4].i(2) == doctest::Approx(test::i_shunt)); - } -} - -TEST_CASE_TEMPLATE( - "Test main model - update load and shunt param", settings, regular_update, - cached_update) { // TODO(mgovers): we should whitebox-test this instead; values not reproduced by validation tests - State state; - auto main_model = default_model(state); - - state.sym_load_update[0].p_specified = 2.5e6; - ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, - state.sym_load_update.data()); - update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, - state.asym_load_update.data()); - update_data.add_buffer("shunt", state.shunt_update.size(), state.shunt_update.size(), nullptr, - state.shunt_update.data()); - main_model.update_components(update_data); - - SUBCASE("Symmetrical") { - auto const solver_output = - main_model.calculate(get_default_options(symmetric, CalculationMethod::linear)); - main_model.output_result(solver_output, state.sym_node); - main_model.output_result(solver_output, state.sym_branch); - main_model.output_result(solver_output, state.sym_appliance); - CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[1].u_pu == doctest::Approx(test::u1)); - CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); - CHECK(state.sym_branch[0].i_from == doctest::Approx(test::i)); - CHECK(state.sym_appliance[0].i == doctest::Approx(test::i)); - CHECK(state.sym_appliance[1].i == doctest::Approx(0.0)); - CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load * 2 + test::i_shunt)); - CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); - CHECK(state.sym_appliance[4].i == doctest::Approx(0.0)); - } - SUBCASE("Asymmetrical") { - auto const solver_output = main_model.calculate( - get_default_options(asymmetric, CalculationMethod::linear)); - main_model.output_result(solver_output, state.asym_node); - main_model.output_result(solver_output, state.asym_branch); - main_model.output_result(solver_output, state.asym_appliance); - CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); - CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); - CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); - CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(test::i)); - CHECK(state.asym_appliance[0].i(1) == doctest::Approx(test::i)); - CHECK(state.asym_appliance[1].i(2) == doctest::Approx(0.0)); - CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2 + test::i_shunt)); - CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); - CHECK(state.asym_appliance[4].i(2) == doctest::Approx(0.0)); - } -} - -TEST_CASE_TEMPLATE( - "Test main model - all updates", settings, regular_update, - cached_update) { // TODO(mgovers): we should whitebox-test this instead; values not reproduced by validation tests - State state; - auto main_model = default_model(state); - - state.sym_load_update[0].p_specified = 2.5e6; - ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, - state.sym_load_update.data()); - update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, - state.asym_load_update.data()); - update_data.add_buffer("shunt", state.shunt_update.size(), state.shunt_update.size(), nullptr, - state.shunt_update.data()); - update_data.add_buffer("source", state.source_update.size(), state.source_update.size(), nullptr, - state.source_update.data()); - update_data.add_buffer("link", state.link_update.size(), state.link_update.size(), nullptr, - state.link_update.data()); - update_data.add_buffer("fault", state.fault_update.size(), state.fault_update.size(), nullptr, - state.fault_update.data()); - - main_model.update_components(update_data); - - SUBCASE("Symmetrical") { - auto const solver_output = - main_model.calculate(get_default_options(symmetric, CalculationMethod::linear)); - main_model.output_result(solver_output, state.sym_node); - main_model.output_result(solver_output, state.sym_branch); - main_model.output_result(solver_output, state.sym_appliance); - CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[1].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); - CHECK(state.sym_branch[0].i_from == doctest::Approx(0.0).epsilon(1e-6)); - CHECK(state.sym_appliance[0].i == doctest::Approx(0.0).epsilon(1e-6)); - CHECK(state.sym_appliance[1].i == doctest::Approx(test::i)); - CHECK(state.sym_appliance[2].i == doctest::Approx(test::i)); - CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); - CHECK(state.sym_appliance[4].i == doctest::Approx(0.0)); - } - SUBCASE("Asymmetrical") { - auto const solver_output = main_model.calculate( - get_default_options(asymmetric, CalculationMethod::linear)); - main_model.output_result(solver_output, state.asym_node); - main_model.output_result(solver_output, state.asym_branch); - main_model.output_result(solver_output, state.asym_appliance); - CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); - CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(1.05)); - CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); - CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(0.0).epsilon(1e-6)); - CHECK(state.asym_appliance[0].i(1) == doctest::Approx(0.0).epsilon(1e-6)); - CHECK(state.asym_appliance[1].i(2) == doctest::Approx(test::i)); - CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i)); - CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); - CHECK(state.asym_appliance[4].i(2) == doctest::Approx(0.0)); - } -} - -TEST_CASE_TEMPLATE("Test main model - single permanent update from batch", settings, regular_update, - cached_update) { // TODO(mgovers): we should whitebox-test this instead - State state; - auto main_model = default_model(state); - - state.batch_sym_load_update[0].p_specified = 2.5e6; - ConstDataset update_data{true, 5, "update", meta_data::meta_data_gen::meta_data}; - update_data.add_buffer("sym_load", 1, state.batch_sym_load_update.size(), nullptr, - state.batch_sym_load_update.data()); - update_data.add_buffer("asym_load", 1, state.batch_asym_load_update.size(), nullptr, - state.batch_asym_load_update.data()); - update_data.add_buffer("shunt", 1, state.batch_shunt_update.size(), nullptr, state.batch_shunt_update.data()); - update_data.add_buffer("source", 1, state.batch_source_update.size(), nullptr, state.batch_source_update.data()); - update_data.add_buffer("link", 1, state.batch_link_update.size(), nullptr, state.batch_link_update.data()); - update_data.add_buffer("fault", 1, state.batch_fault_update.size(), nullptr, state.batch_fault_update.data()); - - main_model.update_components(update_data); - - SUBCASE("Symmetrical") { - auto const solver_output = - main_model.calculate(get_default_options(symmetric, CalculationMethod::linear)); - main_model.output_result(solver_output, state.sym_node); - main_model.output_result(solver_output, state.sym_branch); - main_model.output_result(solver_output, state.sym_appliance); - CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[1].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); - CHECK(state.sym_branch[0].i_from == doctest::Approx(0.0).epsilon(1e-6)); - CHECK(state.sym_appliance[0].i == doctest::Approx(0.0).epsilon(1e-6)); - CHECK(state.sym_appliance[1].i == doctest::Approx(test::i)); - CHECK(state.sym_appliance[2].i == doctest::Approx(test::i)); - CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); - CHECK(state.sym_appliance[4].i == doctest::Approx(0.0)); - } - SUBCASE("Asymmetrical") { - auto const solver_output = main_model.calculate( - get_default_options(asymmetric, CalculationMethod::linear)); - main_model.output_result(solver_output, state.asym_node); - main_model.output_result(solver_output, state.asym_branch); - main_model.output_result(solver_output, state.asym_appliance); - CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); - CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(1.05)); - CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); - CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(0.0).epsilon(1e-6)); - CHECK(state.asym_appliance[0].i(1) == doctest::Approx(0.0).epsilon(1e-6)); - CHECK(state.asym_appliance[1].i(2) == doctest::Approx(test::i)); - CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i)); - CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); - CHECK(state.asym_appliance[4].i(2) == doctest::Approx(0.0)); - } -} - -TEST_CASE_TEMPLATE("Test main model - restore components", settings, regular_update, - cached_update) { // TODO(mgovers): either whitebox (as a sub-part of batch impl) or otherwise drop - // entirely (tested by batch update) - State state; - auto main_model = default_model(state); - - auto const solver_output_orig = - main_model.calculate(get_default_options(symmetric, CalculationMethod::linear)); - - ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, - state.sym_load_update.data()); - update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, - state.asym_load_update.data()); - - main_model.update_components(update_data); - main_model.restore_components(update_data); - - SUBCASE("Symmetrical") { - auto const solver_output_result = - main_model.calculate(get_default_options(symmetric, CalculationMethod::linear)); - main_model.output_result(solver_output_result, state.sym_node); - main_model.output_result(solver_output_result, state.sym_branch); - main_model.output_result(solver_output_result, state.sym_appliance); - - CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[1].u_pu == doctest::Approx(test::u1)); - CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); - CHECK(state.sym_branch[0].i_from == doctest::Approx(test::i)); - CHECK(state.sym_appliance[0].i == doctest::Approx(test::i)); - CHECK(state.sym_appliance[1].i == doctest::Approx(0.0)); - if constexpr (settings::update_type::value) { - CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load)); - CHECK(state.sym_appliance[3].i == doctest::Approx(test::i_load)); - } else { - CHECK(state.sym_appliance[2].i == doctest::Approx(test::i_load * 2)); - CHECK(state.sym_appliance[3].i == doctest::Approx(0.0)); - } - CHECK(state.sym_appliance[4].i == doctest::Approx(test::i_shunt)); - } - SUBCASE("Asymmetrical") { - auto const solver_output = main_model.calculate( - get_default_options(asymmetric, CalculationMethod::linear)); - main_model.output_result(solver_output, state.asym_node); - main_model.output_result(solver_output, state.asym_branch); - main_model.output_result(solver_output, state.asym_appliance); - - CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); - CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); - CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); - CHECK(state.asym_branch[0].i_from(0) == doctest::Approx(test::i)); - CHECK(state.asym_appliance[0].i(1) == doctest::Approx(test::i)); - CHECK(state.asym_appliance[1].i(2) == doctest::Approx(0.0)); - if constexpr (settings::update_type::value) { - CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load)); - CHECK(state.asym_appliance[3].i(1) == doctest::Approx(test::i_load)); - } else { - CHECK(state.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2)); - CHECK(state.asym_appliance[3].i(1) == doctest::Approx(0.0)); - } - CHECK(state.asym_appliance[4].i(2) == doctest::Approx(test::i_shunt)); - } -} - -TEST_CASE_TEMPLATE("Test main model - updates w/ alternating compute mode", settings, regular_update, - cached_update) { // TODO(mgovers): move to api tests; not possible with current validation framework - constexpr auto check_sym = [](MainModel const& model_, auto const& math_output_) { - State state_; - model_.output_result(math_output_, state_.sym_node); - model_.output_result(math_output_, state_.sym_branch); - model_.output_result(math_output_, state_.sym_appliance); - - CHECK(state_.sym_node[0].u_pu == doctest::Approx(1.05)); - CHECK(state_.sym_node[1].u_pu == doctest::Approx(test::u1)); - CHECK(state_.sym_node[2].u_pu == doctest::Approx(test::u1)); - CHECK(state_.sym_branch[0].i_from == doctest::Approx(test::i)); - CHECK(state_.sym_appliance[0].i == doctest::Approx(test::i)); - CHECK(state_.sym_appliance[1].i == doctest::Approx(0.0)); - CHECK(state_.sym_appliance[2].i == doctest::Approx(test::i_load * 2 + test::i_shunt)); - CHECK(state_.sym_appliance[3].i == doctest::Approx(0.0)); - CHECK(state_.sym_appliance[4].i == doctest::Approx(0.0)); - }; - constexpr auto check_asym = [](MainModel const& model_, auto const& math_output_) { - State state_; - model_.output_result(math_output_, state_.asym_node); - model_.output_result(math_output_, state_.asym_branch); - model_.output_result(math_output_, state_.asym_appliance); - CHECK(state_.asym_node[0].u_pu(0) == doctest::Approx(1.05)); - CHECK(state_.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); - CHECK(state_.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); - CHECK(state_.asym_branch[0].i_from(0) == doctest::Approx(test::i)); - CHECK(state_.asym_appliance[0].i(1) == doctest::Approx(test::i)); - CHECK(state_.asym_appliance[1].i(2) == doctest::Approx(0.0)); - CHECK(state_.asym_appliance[2].i(0) == doctest::Approx(test::i_load * 2 + test::i_shunt)); - CHECK(state_.asym_appliance[3].i(1) == doctest::Approx(0.0)); - CHECK(state_.asym_appliance[4].i(2) == doctest::Approx(0.0)); - }; - - State state; - auto main_model = default_model(state); - - state.sym_load_update[0].p_specified = 2.5e6; - - ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, - state.sym_load_update.data()); - update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, - state.asym_load_update.data()); - update_data.add_buffer("shunt", state.shunt_update.size(), state.shunt_update.size(), nullptr, - state.shunt_update.data()); - - // This will lead to no topo change but param change - main_model.update_components(update_data); - - auto const math_output_sym_1 = - main_model.calculate(get_default_options(symmetric, CalculationMethod::linear)); - check_sym(main_model, math_output_sym_1); - - auto const math_output_asym_1 = - main_model.calculate(get_default_options(asymmetric, CalculationMethod::linear)); - check_asym(main_model, math_output_asym_1); - - SUBCASE("No new update") { - // Math state may be fully cached - } - if constexpr (std::same_as) { - SUBCASE("No new parameter change") { - // Math state may be fully cached due to no change - main_model.update_components(update_data); - } - } - SUBCASE("With parameter change") { - // Restore to original state and re-apply same update: causes param change for cached update - main_model.restore_components(update_data); - main_model.update_components(update_data); - } - - auto const math_output_asym_2 = - main_model.calculate(get_default_options(asymmetric, CalculationMethod::linear)); - check_asym(main_model, math_output_asym_2); - - auto const math_output_sym_2 = - main_model.calculate(get_default_options(symmetric, CalculationMethod::linear)); - check_sym(main_model, math_output_sym_2); - - main_model.restore_components(update_data); -} - -namespace { -auto incomplete_input_model(State const& state) -> MainModel { - MainModel main_model{50.0, meta_data::meta_data_gen::meta_data}; - - std::vector const incomplete_source_input{{6, 1, 1, nan, nan, 1e12, nan, nan}, - {10, 3, 1, nan, nan, 1e12, nan, nan}}; - std::vector const incomplete_sym_load_input{{7, 3, 1, LoadGenType::const_y, nan, 0.0}}; - std::vector const incomplete_asym_load_input{ - {8, 3, 1, LoadGenType::const_y, RealValue{nan}, RealValue{0.0}}}; - - main_model.add_component(state.node_input); - main_model.add_component(state.line_input); - main_model.add_component(state.link_input); - main_model.add_component(incomplete_source_input); - main_model.add_component(incomplete_sym_load_input); - main_model.add_component(incomplete_asym_load_input); - main_model.add_component(state.shunt_input); - main_model.set_construction_complete(); - - return main_model; -} -} // namespace - -TEST_CASE("Test main model - incomplete input") { - using CalculationMethod::iterative_current; - using CalculationMethod::linear; - using CalculationMethod::linear_current; - using CalculationMethod::newton_raphson; - - State const state; - auto main_model = default_model(state); - auto test_model = incomplete_input_model(state); - - std::vector complete_source_update{{6, 1, 1.05, nan}, {10, 1, 1.05, 0}}; - std::vector complete_sym_load_update{{7, 1, 0.5e6, nan}}; - std::vector complete_asym_load_update{ - {8, 1, RealValue{0.5e6 / 3.0}, RealValue{nan}}}; - - ConstDataset update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data.add_buffer("source", complete_source_update.size(), complete_source_update.size(), nullptr, - complete_source_update.data()); - update_data.add_buffer("sym_load", complete_sym_load_update.size(), complete_sym_load_update.size(), nullptr, - complete_sym_load_update.data()); - update_data.add_buffer("asym_load", complete_asym_load_update.size(), complete_asym_load_update.size(), nullptr, - complete_asym_load_update.data()); - - std::vector incomplete_source_update{{6, na_IntS, nan, nan}, {10, na_IntS, nan, nan}}; - std::vector incomplete_sym_load_update{{7, na_IntS, nan, nan}}; - std::vector incomplete_asym_load_update{ - {8, na_IntS, RealValue{nan}, RealValue{nan}}}; - - ConstDataset incomplete_update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - incomplete_update_data.add_buffer("source", incomplete_source_update.size(), incomplete_source_update.size(), - nullptr, incomplete_source_update.data()); - incomplete_update_data.add_buffer("sym_load", incomplete_sym_load_update.size(), incomplete_sym_load_update.size(), - nullptr, incomplete_sym_load_update.data()); - incomplete_update_data.add_buffer("asym_load", incomplete_asym_load_update.size(), - incomplete_asym_load_update.size(), nullptr, incomplete_asym_load_update.data()); - - MainModel const ref_model{main_model}; - - SUBCASE("Asymmetrical - Complete") { // TODO(mgovers): no validation case for asym exists - MutableDataset test_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; - MutableDataset ref_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; - - std::vector> test_asym_node(state.asym_node.size()); - std::vector> ref_asym_node(state.asym_node.size()); - test_result_data.add_buffer("node", test_asym_node.size(), test_asym_node.size(), nullptr, - test_asym_node.data()); - ref_result_data.add_buffer("node", ref_asym_node.size(), ref_asym_node.size(), nullptr, ref_asym_node.data()); - - SUBCASE("Test linear calculation") { - test_model.calculate(get_default_options(asymmetric, linear), test_result_data, update_data); - main_model.calculate(get_default_options(asymmetric, linear), ref_result_data, update_data); - } - - SUBCASE("Test linear current calculation") { - test_model.calculate(get_default_options(asymmetric, linear_current), test_result_data, update_data); - main_model.calculate(get_default_options(asymmetric, linear_current), ref_result_data, update_data); - } - - SUBCASE("Test iterative current calculation") { - test_model.calculate(get_default_options(asymmetric, iterative_current), test_result_data, update_data); - main_model.calculate(get_default_options(asymmetric, iterative_current), ref_result_data, update_data); - } - - SUBCASE("Test iterative Newton-Rhapson calculation") { - test_model.calculate(get_default_options(asymmetric, newton_raphson), test_result_data, update_data); - main_model.calculate(get_default_options(asymmetric, newton_raphson), ref_result_data, update_data); - } - - for (auto component_idx : {0, 1, 2}) { - CAPTURE(component_idx); - - for (auto phase_idx : {0, 1, 2}) { - CAPTURE(phase_idx); - - CHECK(test_asym_node[component_idx].u_pu(phase_idx) == - doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx))); - } - } - } - - SUBCASE("Symmetrical - Incomplete") { // TODO(mgovers): not tested elsewhere; maybe test in API model? - MutableDataset test_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - MutableDataset const ref_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - - std::vector> test_sym_node(state.sym_node.size()); - test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, test_sym_node.data()); - - SUBCASE("Target dataset") { - CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - .calculation_symmetry = symmetric, - .calculation_method = linear, - .err_tol = 1e-8, - .max_iter = 1}, - test_result_data), - SparseMatrixError); - } - SUBCASE("Empty update dataset") { - ConstDataset const update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - - CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - .calculation_symmetry = symmetric, - .calculation_method = linear, - .err_tol = 1e-8, - .max_iter = 1}, - test_result_data, update_data), - SparseMatrixError); - } - SUBCASE("Update dataset") { - CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - .calculation_symmetry = symmetric, - .calculation_method = linear, - .err_tol = 1e-8, - .max_iter = 1}, - test_result_data, incomplete_update_data), - BatchCalculationError); - } - } - - SUBCASE("Asymmetrical - Incomplete") { // TODO(mgovers): not tested elsewhere; maybe test in API model? - MutableDataset test_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; - MutableDataset const ref_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; - - std::vector> test_sym_node(state.sym_node.size()); - test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, test_sym_node.data()); - - SUBCASE("Target dataset") { - CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - .calculation_symmetry = asymmetric, - .calculation_method = linear, - .err_tol = 1e-8, - .max_iter = 1}, - test_result_data), - SparseMatrixError); - } - SUBCASE("Empty update dataset") { - ConstDataset const update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - - CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - .calculation_symmetry = asymmetric, - .calculation_method = linear, - .err_tol = 1e-8, - .max_iter = 1}, - test_result_data, update_data), - SparseMatrixError); - } - SUBCASE("Update dataset") { - CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - .calculation_symmetry = asymmetric, - .calculation_method = linear, - .err_tol = 1e-8, - .max_iter = 1}, - test_result_data, incomplete_update_data), - BatchCalculationError); - } - } -} - -TEST_CASE("Test main model - Incomplete followed by complete") { // TODO(mgovers): This tests the reset of 2 consecutive - // batch scenarios and definitely needs to be tested - using CalculationMethod::linear; - - State const state; - auto main_model = default_model(state); - auto test_model = incomplete_input_model(state); - - constexpr Idx batch_size = 2; - - std::vector mixed_source_update{ - {6, 1, nan, nan}, {10, 1, nan, nan}, {6, 1, 1.05, nan}, {10, 1, 1.05, 0}}; - std::vector mixed_sym_load_update{{7, 1, nan, 1.0}, {7, 1, 0.5e6, nan}}; - std::vector mixed_asym_load_update{ - {8, 1, RealValue{nan}, RealValue{1.0}}, - {8, 1, RealValue{0.5e6 / 3.0}, RealValue{nan}}}; - - auto const source_indptr = IdxVector{0, 0, static_cast(mixed_source_update.size())}; - - REQUIRE(source_indptr.size() == batch_size + 1); - - ConstDataset mixed_update_data{true, batch_size, "update", meta_data::meta_data_gen::meta_data}; - mixed_update_data.add_buffer("source", 2, 4, nullptr, mixed_source_update.data()); - mixed_update_data.add_buffer("sym_load", 1, 2, nullptr, mixed_sym_load_update.data()); - mixed_update_data.add_buffer("asym_load", 1, 2, nullptr, mixed_asym_load_update.data()); - - ConstDataset second_scenario_update_data{false, 1, "update", meta_data::meta_data_gen::meta_data}; - second_scenario_update_data.add_buffer("source", 2, 2, nullptr, mixed_source_update.data() + 2); - second_scenario_update_data.add_buffer("sym_load", 1, 1, nullptr, mixed_sym_load_update.data() + 1); - second_scenario_update_data.add_buffer("asym_load", 1, 1, nullptr, mixed_asym_load_update.data() + 1); - - SUBCASE("Symmetrical") { - MutableDataset test_result_data{true, batch_size, "sym_output", meta_data::meta_data_gen::meta_data}; - MutableDataset ref_result_data{false, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - - std::vector> test_sym_node(batch_size * state.sym_node.size(), - {na_IntID, na_IntS, nan, nan, nan, nan, nan}); - std::vector> ref_sym_node(state.sym_node.size(), - {na_IntID, na_IntS, nan, nan, nan, nan, nan}); - test_result_data.add_buffer("node", state.sym_node.size(), test_sym_node.size(), nullptr, test_sym_node.data()); - ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), nullptr, ref_sym_node.data()); - - CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - .calculation_symmetry = symmetric, - .calculation_method = linear, - .err_tol = 1e-8, - .max_iter = 1}, - test_result_data, mixed_update_data), - BatchCalculationError); - main_model.calculate({.calculation_type = power_flow, - .calculation_symmetry = symmetric, - .calculation_method = linear, - .err_tol = 1e-8, - .max_iter = 1}, - ref_result_data, second_scenario_update_data); - - CHECK(is_nan(test_sym_node[0].u_pu)); - CHECK(is_nan(test_sym_node[1].u_pu)); - CHECK(is_nan(test_sym_node[2].u_pu)); - CHECK(test_sym_node[state.sym_node.size() + 0].u_pu == doctest::Approx(ref_sym_node[0].u_pu)); - CHECK(test_sym_node[state.sym_node.size() + 1].u_pu == doctest::Approx(ref_sym_node[1].u_pu)); - CHECK(test_sym_node[state.sym_node.size() + 2].u_pu == doctest::Approx(ref_sym_node[2].u_pu)); - } - - SUBCASE("Asymmetrical") { - MutableDataset test_result_data{true, batch_size, "asym_output", meta_data::meta_data_gen::meta_data}; - MutableDataset ref_result_data{false, 1, "asym_output", meta_data::meta_data_gen::meta_data}; - - std::vector> test_asym_node( - batch_size * state.sym_node.size(), - {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, - RealValue{nan}, RealValue{nan}, RealValue{nan}}); - std::vector> ref_asym_node( - state.sym_node.size(), - {na_IntID, na_IntS, RealValue{nan}, RealValue{nan}, - RealValue{nan}, RealValue{nan}, RealValue{nan}}); - test_result_data.add_buffer("node", state.sym_node.size(), test_asym_node.size(), nullptr, - test_asym_node.data()); - ref_result_data.add_buffer("node", ref_asym_node.size(), ref_asym_node.size(), nullptr, ref_asym_node.data()); - - CHECK_THROWS_AS(test_model.calculate({.calculation_type = power_flow, - .calculation_symmetry = asymmetric, - .calculation_method = linear, - .err_tol = 1e-8, - .max_iter = 1}, - test_result_data, mixed_update_data), - BatchCalculationError); - main_model.calculate({.calculation_type = power_flow, - .calculation_symmetry = asymmetric, - .calculation_method = linear, - .err_tol = 1e-8, - .max_iter = 1}, - ref_result_data, second_scenario_update_data); - - for (auto component_idx : {0, 1, 2}) { - CAPTURE(component_idx); - - CHECK(is_nan(test_asym_node[component_idx].u_pu)); - - for (auto phase_idx : {0, 1, 2}) { - CAPTURE(phase_idx); - - CHECK(test_asym_node[state.asym_node.size() + component_idx].u_pu(phase_idx) == - doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx))); - } - } - } -} - -} // namespace power_grid_model From 749e64d8ffd2d51dea61a0c66a80cf09218a67e8 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 16:38:45 +0100 Subject: [PATCH 19/49] remove deprecated functionality; make more main model private Signed-off-by: Martijn Govers --- .../include/power_grid_model/main_model.hpp | 3 - .../power_grid_model/main_model_impl.hpp | 58 +++++++++---------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp index cc749d64a..2562e8813 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp @@ -55,9 +55,6 @@ class MainModel { } void set_construction_complete() { impl().set_construction_complete(); } - void restore_components(ConstDataset const& update_data) { - impl().restore_components(impl().get_all_sequence_idx_map(update_data)); - } template void add_component(std::vector const& components) { add_component(std::span{components}); diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_model_impl.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_model_impl.hpp index 88324ffc0..412afda18 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_model_impl.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_model_impl.hpp @@ -306,35 +306,6 @@ class MainModelImpl, ComponentLis update_components(update_data, 0, sequence_idx_map); } - template void restore_component(SequenceIdxView const& sequence_idx) { - constexpr auto component_index = main_core::utils::index_of_component; - - auto& cached_inverse_update = std::get(cached_inverse_update_); - auto const& component_sequence = std::get(sequence_idx); - - if (!cached_inverse_update.empty()) { - update_component(cached_inverse_update, component_sequence); - cached_inverse_update.clear(); - } - } - - // restore the initial values of all components - void restore_components(SequenceIdxView const& sequence_idx) { - (restore_component(sequence_idx), ...); - - update_state(cached_state_changes_); - cached_state_changes_ = {}; - } - void restore_components(std::array const>, - main_core::utils::n_types> const& sequence_idx) { - restore_components(std::array{std::span{ - std::get>(sequence_idx).get()}...}); - } - void restore_components(main_core::utils::SequenceIdx const& sequence_idx) { - restore_components(std::array{std::span{ - std::get>(sequence_idx)}...}); - } - // set complete construction // initialize internal arrays void set_construction_complete() { @@ -402,6 +373,35 @@ class MainModelImpl, ComponentLis is_asym_parameter_up_to_date_ = is_asym_parameter_up_to_date_ && !changes.topo && !changes.param; } + template void restore_component(SequenceIdxView const& sequence_idx) { + constexpr auto component_index = main_core::utils::index_of_component; + + auto& cached_inverse_update = std::get(cached_inverse_update_); + auto const& component_sequence = std::get(sequence_idx); + + if (!cached_inverse_update.empty()) { + update_component(cached_inverse_update, component_sequence); + cached_inverse_update.clear(); + } + } + + // restore the initial values of all components + void restore_components(SequenceIdxView const& sequence_idx) { + (restore_component(sequence_idx), ...); + + update_state(cached_state_changes_); + cached_state_changes_ = {}; + } + void restore_components(std::array const>, + main_core::utils::n_types> const& sequence_idx) { + restore_components(std::array{std::span{ + std::get>(sequence_idx).get()}...}); + } + void restore_components(main_core::utils::SequenceIdx const& sequence_idx) { + restore_components(std::array{std::span{ + std::get>(sequence_idx)}...}); + } + template requires std::invocable, Idx /*n_math_solvers*/> && From f4c39204c41d28295943ecc0ff4eee5ef04cfd51 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 16:40:47 +0100 Subject: [PATCH 20/49] merge api model misc into api model update tests Signed-off-by: Martijn Govers --- tests/native_api_tests/CMakeLists.txt | 1 - .../native_api_tests/test_api_model_misc.cpp | 712 ------------------ .../test_api_model_update.cpp | 653 +++++++++++++++- 3 files changed, 651 insertions(+), 715 deletions(-) delete mode 100644 tests/native_api_tests/test_api_model_misc.cpp diff --git a/tests/native_api_tests/CMakeLists.txt b/tests/native_api_tests/CMakeLists.txt index b09b4eb72..f82dcd743 100644 --- a/tests/native_api_tests/CMakeLists.txt +++ b/tests/native_api_tests/CMakeLists.txt @@ -8,7 +8,6 @@ set(PROJECT_SOURCES "test_api_buffer.cpp" "test_api_model.cpp" "test_api_model_update.cpp" - "test_api_model_misc.cpp" "test_api_serialization.cpp" "test_api_utils.cpp" ) diff --git a/tests/native_api_tests/test_api_model_misc.cpp b/tests/native_api_tests/test_api_model_misc.cpp deleted file mode 100644 index a46eb7156..000000000 --- a/tests/native_api_tests/test_api_model_misc.cpp +++ /dev/null @@ -1,712 +0,0 @@ -// SPDX-FileCopyrightText: Contributors to the Power Grid Model project -// -// SPDX-License-Identifier: MPL-2.0 - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -namespace power_grid_model_cpp { -namespace { -using std::numbers::pi; -using std::numbers::sqrt3; - -constexpr Idx default_option{-1}; -constexpr double deg_120 = 2.0 / 3.0 * pi; -constexpr double deg_240 = 4.0 / 3.0 * pi; - -enum class CalculationSymmetry : Idx { symmetric = PGM_symmetric, asymmetric = PGM_asymmetric }; -enum class LoadGenType : IntS { - const_pq = 0, // constant power - const_y = 1, // constant element_admittance (impedance) - const_i = 2, // constant current -}; -enum class MeasuredTerminalType : IntS { - branch_from = 0, - branch_to = 1, - source = 2, - shunt = 3, - load = 4, - generator = 5, - branch3_1 = 6, - branch3_2 = 7, - branch3_3 = 8, - node = 9 -}; - -Options get_default_options(PGM_SymmetryType calculation_symmetry, PGM_CalculationMethod calculation_method, - Idx threading = default_option) { - Options opt; - opt.set_calculation_type(PGM_power_flow); - opt.set_symmetric(calculation_symmetry); - opt.set_calculation_method(calculation_method); - if (threading != default_option) { - opt.set_threading(threading); - } - return opt; -} - -// struct regular_update { -// using update_type = permanent_update_t; -// }; - -// struct cached_update { -// using update_type = cached_update_t; -// }; - -namespace test { -constexpr double z_bus_2 = 1.0 / (0.015 + 0.5e6 / 10e3 / 10e3 * 2); -constexpr double z_total = z_bus_2 + 10.0; -constexpr double u1 = 1.05 * z_bus_2 / (z_bus_2 + 10.0); -constexpr double i = 1.05 * 10e3 / z_total / sqrt3; -constexpr double i_shunt = 0.015 / 0.025 * i; -constexpr double i_load = 0.005 / 0.025 * i; -} // namespace test - -struct State { - std::vector node_id{1, 2, 3}; - std::vector node_u_rated{10e3, 10e3, 10e3}; - - std::vector line_id{4}; - std::vector line_from_node{1}; - std::vector line_to_node{2}; - std::vector line_from_status{1}; - std::vector line_to_status{1}; - std::vector line_r1{10.0}; - std::vector line_x1{0.0}; - std::vector line_c1{0.0}; - std::vector line_tan1{0.0}; - std::vector line_r0{10.0}; - std::vector line_x0{0.0}; - std::vector line_c0{0.0}; - std::vector line_tan0{0.0}; - std::vector line_i_n{1e3}; - - std::vector link_id{5}; - std::vector link_from_node{2}; - std::vector link_to_node{3}; - std::vector link_from_status{1}; - std::vector link_to_status{1}; - - std::vector source_id{6, 10}; - std::vector source_node{1, 3}; - std::vector source_status{1, 0}; - std::vector source_u_ref{1.05, 1.05}; - std::vector source_u_ref_angle{nan, 0.0}; - std::vector source_sk{1e12, 1e12}; - - std::vector sym_load_id{7}; - std::vector sym_load_node{3}; - std::vector sym_load_status{1}; - std::vector sym_load_type{LoadGenType::const_y}; - std::vector sym_load_p_specified{0.5e6}; - std::vector sym_load_q_specified{0.0}; - - std::vector asym_load_id{8}; - std::vector asym_load_node{3}; - std::vector asym_load_status{1}; - std::vector asym_load_type{LoadGenType::const_y}; - std::vector asym_load_p_specified{0.5e6 / 3.0, 0.5e6 / 3.0, 0.5e6 / 3.0}; - std::vector asym_load_q_specified{0.0, 0.0, 0.0}; - - std::vector shunt_id{9}; - std::vector shunt_node{3}; - std::vector shunt_status{1}; - std::vector shunt_g1{0.015}; - std::vector shunt_b1{0.0}; - std::vector shunt_g0{0.015}; - std::vector shunt_b0{0.0}; - - auto get_input_dataset() const { - DatasetConst result{"input", 0, 1}; - - result.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); - result.add_attribute_buffer("node", "id", node_id.data()); - result.add_attribute_buffer("node", "u_rated", node_u_rated.data()); - - result.add_buffer("line", line_id.size(), line_id.size(), nullptr, nullptr); - result.add_attribute_buffer("line", "id", line_id.data()); - result.add_attribute_buffer("line", "from_node", line_from_node.data()); - result.add_attribute_buffer("line", "to_node", line_to_node.data()); - result.add_attribute_buffer("line", "from_status", line_from_status.data()); - result.add_attribute_buffer("line", "to_status", line_to_status.data()); - result.add_attribute_buffer("line", "r1", line_r1.data()); - result.add_attribute_buffer("line", "x1", line_x1.data()); - result.add_attribute_buffer("line", "c1", line_c1.data()); - result.add_attribute_buffer("line", "tan1", line_tan1.data()); - result.add_attribute_buffer("line", "r0", line_r0.data()); - result.add_attribute_buffer("line", "x0", line_x0.data()); - result.add_attribute_buffer("line", "c0", line_c0.data()); - result.add_attribute_buffer("line", "tan0", line_tan0.data()); - result.add_attribute_buffer("line", "i_n", line_i_n.data()); - - result.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); - result.add_attribute_buffer("link", "id", link_id.data()); - result.add_attribute_buffer("link", "from_node", link_from_node.data()); - result.add_attribute_buffer("link", "to_node", link_to_node.data()); - result.add_attribute_buffer("link", "from_status", link_from_status.data()); - result.add_attribute_buffer("link", "to_status", link_to_status.data()); - - result.add_buffer("source", source_id.size(), source_id.size(), nullptr, nullptr); - result.add_attribute_buffer("source", "id", source_id.data()); - result.add_attribute_buffer("source", "node", source_node.data()); - result.add_attribute_buffer("source", "status", source_status.data()); - result.add_attribute_buffer("source", "u_ref", source_u_ref.data()); - result.add_attribute_buffer("source", "u_ref_angle", source_u_ref_angle.data()); - result.add_attribute_buffer("source", "sk", source_sk.data()); - - result.add_buffer("sym_load", sym_load_id.size(), sym_load_id.size(), nullptr, nullptr); - result.add_attribute_buffer("sym_load", "id", sym_load_id.data()); - result.add_attribute_buffer("sym_load", "node", sym_load_node.data()); - result.add_attribute_buffer("sym_load", "status", sym_load_status.data()); - result.add_attribute_buffer("sym_load", "type", sym_load_type.data()); - result.add_attribute_buffer("sym_load", "p_specified", sym_load_p_specified.data()); - result.add_attribute_buffer("sym_load", "q_specified", sym_load_q_specified.data()); - - result.add_buffer("asym_load", asym_load_id.size(), asym_load_id.size(), nullptr, nullptr); - result.add_attribute_buffer("asym_load", "id", asym_load_id.data()); - result.add_attribute_buffer("asym_load", "node", asym_load_node.data()); - result.add_attribute_buffer("asym_load", "status", asym_load_status.data()); - result.add_attribute_buffer("asym_load", "type", asym_load_type.data()); - result.add_attribute_buffer("asym_load", "p_specified", asym_load_p_specified.data()); - result.add_attribute_buffer("asym_load", "q_specified", asym_load_q_specified.data()); - - result.add_buffer("shunt", shunt_id.size(), shunt_id.size(), nullptr, nullptr); - result.add_attribute_buffer("shunt", "id", shunt_id.data()); - result.add_attribute_buffer("shunt", "node", shunt_node.data()); - result.add_attribute_buffer("shunt", "status", shunt_status.data()); - result.add_attribute_buffer("shunt", "g1", shunt_g1.data()); - result.add_attribute_buffer("shunt", "b1", shunt_b1.data()); - result.add_attribute_buffer("shunt", "g0", shunt_g0.data()); - result.add_attribute_buffer("shunt", "b0", shunt_b0.data()); - - return result; - } - - // // output vector - // std::vector> sym_node = std::vector>(3); - // std::vector> sym_branch = std::vector>(2); - // std::vector> sym_appliance = std::vector>(5); - // std::vector> asym_node = std::vector>(3); - // std::vector> asym_branch = std::vector>(2); - // std::vector> asym_appliance = std::vector>(5); - - // // individual symmetric - // std::vector> sym_line = std::vector>(1); - // std::vector> sym_link = std::vector>(1); - // std::vector> sym_load_sym = std::vector>(1); - // std::vector> sym_load_asym = std::vector>(1); - // std::vector> sym_source = std::vector>(2); - // std::vector> sym_shunt = std::vector>(1); - - // // individual asymmetric - // std::vector> asym_line = std::vector>(1); - // std::vector> asym_link = std::vector>(1); - // std::vector> asym_load_sym = std::vector>(1); - // std::vector> asym_load_asym = std::vector>(1); - // std::vector> asym_source = std::vector>(2); - // std::vector> asym_shunt = std::vector>(1); - - // // update vector - // std::vector sym_load_update{{7, 1, 1.0e6, nan}}; - // std::vector asym_load_update{{8, 0, RealValue{nan}, - // RealValue{nan}}}; std::vector shunt_update{{9, 0, nan, 0.02, nan, 0.02}}; - // std::vector shunt_update_2{{6, 0, nan, 0.01, nan, 0.01}}; // used for test case alternate compute - // mode std::vector source_update{{10, 1, test::u1, nan}}; std::vector link_update{{5, - // 1, 0}}; - - // // batch update vector - // std::vector batch_sym_load_update{{7, 1, 1.0e6, nan}, {7}, {7}, {7}, {7}}; - // std::vector batch_asym_load_update{ - // {8, 0, RealValue{nan}, RealValue{nan}}, {8}, {8}, {8}, {8}}; - // std::vector batch_shunt_update{{9, 0, nan, 0.02, nan, 0.02}, {9}, {9}, {9}, {9}}; - // std::vector batch_source_update{{10, 1, test::u1, nan}, {10}, {10}, {10}, {10}}; - // std::vector batch_link_update{{5, 1, 0}, {5}, {5}, {5}, {5}}; -}; - -// auto default_model(State const& state) -> Model { return Model{50.0, state.get_input_dataset()}; } -} // namespace - -TEST_CASE("API model - all updates") { - using namespace std::string_literals; - - State state; - auto const input_dataset = state.get_input_dataset(); - auto const& input_info = input_dataset.get_info(); - auto model = Model{50.0, input_dataset}; - - // update vector - std::vector sym_load_update_id{7}; - std::vector sym_load_update_status{1}; - std::vector sym_load_update_p_specified{2.5e6}; - - std::vector asym_load_update_id{8}; - std::vector asym_load_update_status{0}; - - std::vector shunt_update_id{9}; - std::vector shunt_update_status{0}; - std::vector shunt_update_b1{0.02}; - std::vector shunt_update_b0{0.02}; - - // used for test case alternate compute mode - std::vector shunt_update_2_id{6}; - std::vector source_update_2_status{0}; - std::vector shunt_update_2_b1{0.01}; - std::vector shunt_update_2_b0{0.01}; - - std::vector source_update_id{10}; - std::vector source_update_status{1}; - std::vector source_update_u_ref{test::u1}; - - std::vector link_update_id{5}; - std::vector link_update_from_status{1}; - std::vector link_update_to_status{0}; - - DatasetConst update_data{"update", 1, 1}; - update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); - update_data.add_attribute_buffer("sym_load", "id", sym_load_update_id.data()); - update_data.add_attribute_buffer("sym_load", "status", sym_load_update_status.data()); - update_data.add_attribute_buffer("sym_load", "p_specified", sym_load_update_p_specified.data()); - - update_data.add_buffer("asym_load", 1, 1, nullptr, nullptr); - update_data.add_attribute_buffer("asym_load", "id", asym_load_update_id.data()); - update_data.add_attribute_buffer("asym_load", "status", asym_load_update_status.data()); - - update_data.add_buffer("shunt", 1, 1, nullptr, nullptr); - update_data.add_attribute_buffer("shunt", "id", shunt_update_id.data()); - update_data.add_attribute_buffer("shunt", "status", shunt_update_status.data()); - update_data.add_attribute_buffer("shunt", "b1", shunt_update_b1.data()); - update_data.add_attribute_buffer("shunt", "b0", shunt_update_b0.data()); - - update_data.add_buffer("source", 1, 1, nullptr, nullptr); - update_data.add_attribute_buffer("source", "id", source_update_id.data()); - update_data.add_attribute_buffer("source", "status", source_update_status.data()); - update_data.add_attribute_buffer("source", "u_ref", source_update_u_ref.data()); - - update_data.add_buffer("link", 1, 1, nullptr, nullptr); - update_data.add_attribute_buffer("link", "id", link_update_id.data()); - update_data.add_attribute_buffer("link", "from_status", link_update_from_status.data()); - update_data.add_attribute_buffer("link", "to_status", link_update_to_status.data()); - - auto const output_dataset_type = "sym_output"s; - for (Idx comp_type_idx = 0; comp_type_idx < input_info.n_components(); ++comp_type_idx) { - CAPTURE(comp_type_idx); - - auto const comp_type = input_info.component_name(comp_type_idx); - CAPTURE(comp_type); - - auto const comp_meta = MetaData::get_component_by_name(output_dataset_type, comp_type); - auto const total_elements = input_info.component_total_elements(comp_type_idx); - auto const elements_per_scenario = input_info.component_elements_per_scenario(comp_type_idx); - auto const n_bytes = total_elements * MetaData::component_size(comp_meta); - - std::vector sym_output_from_batch(n_bytes); - std::vector sym_output_from_updated_single(n_bytes); - - DatasetMutable output_data_from_batch{output_dataset_type, 1, 1}; - DatasetMutable output_data_from_updated_single{output_dataset_type, false, 1}; - - output_data_from_batch.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, - sym_output_from_batch.data()); - output_data_from_updated_single.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, - sym_output_from_updated_single.data()); - - auto opt = get_default_options(PGM_symmetric, PGM_linear); - model.calculate(opt, output_data_from_batch, update_data); - model.update(update_data); - model.calculate(opt, output_data_from_updated_single); - - CHECK(sym_output_from_batch == sym_output_from_updated_single); - } -} - -TEST_CASE("API model - updates w/ alternating compute mode") { - State state; - auto const input_dataset = state.get_input_dataset(); - auto model = Model{50.0, input_dataset}; - - auto const check_sym = [&] { - std::vector sym_node_output_u_pu(3); - std::vector sym_line_output_i_from(1); - std::vector sym_source_output_i(2); - std::vector sym_sym_load_output_i(1); - std::vector sym_asym_load_output_i(1); - std::vector sym_shunt_output_i(1); - - DatasetMutable sym_output{"sym_output", false, 1}; - sym_output.add_buffer("node", 1, 1, nullptr, nullptr); - sym_output.add_attribute_buffer("node", "u_pu", sym_node_output_u_pu.data()); - - sym_output.add_buffer("line", 1, 1, nullptr, nullptr); - sym_output.add_attribute_buffer("line", "i_from", sym_line_output_i_from.data()); - - sym_output.add_buffer("source", 2, 2, nullptr, nullptr); - sym_output.add_attribute_buffer("source", "i", sym_source_output_i.data()); - - sym_output.add_buffer("sym_load", 1, 1, nullptr, nullptr); - sym_output.add_attribute_buffer("sym_load", "i", sym_sym_load_output_i.data()); - - sym_output.add_buffer("asym_load", 1, 1, nullptr, nullptr); - sym_output.add_attribute_buffer("asym_load", "i", sym_asym_load_output_i.data()); - - sym_output.add_buffer("shunt", 1, 1, nullptr, nullptr); - sym_output.add_attribute_buffer("shunt", "i", sym_shunt_output_i.data()); - - model.calculate(get_default_options(PGM_symmetric, PGM_linear), sym_output); - - CHECK(sym_node_output_u_pu[0] == doctest::Approx(1.05)); - CHECK(sym_node_output_u_pu[1] == doctest::Approx(test::u1)); - CHECK(sym_node_output_u_pu[2] == doctest::Approx(test::u1)); - CHECK(sym_line_output_i_from[0] == doctest::Approx(test::i)); - CHECK(sym_source_output_i[0] == doctest::Approx(test::i)); - CHECK(sym_source_output_i[1] == doctest::Approx(0.0)); - CHECK(sym_sym_load_output_i[0] == doctest::Approx(test::i_load * 2 + test::i_shunt)); - CHECK(sym_asym_load_output_i[0] == doctest::Approx(0.0)); - CHECK(sym_shunt_output_i[0] == doctest::Approx(0.0)); - }; - auto const check_asym = [&] { - std::vector asym_node_output_u_pu(3 * 3); - std::vector asym_line_output_i_from(1 * 3); - std::vector asym_source_output_i(2 * 3); - std::vector asym_sym_load_output_i(1 * 3); - std::vector asym_asym_load_output_i(1 * 3); - std::vector asym_shunt_output_i(1 * 3); - - DatasetMutable asym_output{"asym_output", false, 1}; - asym_output.add_buffer("node", 1, 1, nullptr, nullptr); - asym_output.add_attribute_buffer("node", "u_pu", asym_node_output_u_pu.data()); - - asym_output.add_buffer("line", 1, 1, nullptr, nullptr); - asym_output.add_attribute_buffer("line", "i_from", asym_line_output_i_from.data()); - - asym_output.add_buffer("source", 2, 2, nullptr, nullptr); - asym_output.add_attribute_buffer("source", "i", asym_source_output_i.data()); - - asym_output.add_buffer("sym_load", 1, 1, nullptr, nullptr); - asym_output.add_attribute_buffer("sym_load", "i", asym_sym_load_output_i.data()); - - asym_output.add_buffer("asym_load", 1, 1, nullptr, nullptr); - asym_output.add_attribute_buffer("asym_load", "i", asym_asym_load_output_i.data()); - - asym_output.add_buffer("shunt", 1, 1, nullptr, nullptr); - asym_output.add_attribute_buffer("shunt", "i", asym_shunt_output_i.data()); - - model.calculate(get_default_options(PGM_asymmetric, PGM_linear), asym_output); - - CHECK(asym_node_output_u_pu[0 * 3 + 0] == doctest::Approx(1.05)); - CHECK(asym_node_output_u_pu[1 * 3 + 1] == doctest::Approx(test::u1)); - CHECK(asym_node_output_u_pu[2 * 2 + 1] == doctest::Approx(test::u1)); - CHECK(asym_line_output_i_from[0] == doctest::Approx(test::i)); - CHECK(asym_source_output_i[0 * 3 + 1] == doctest::Approx(test::i)); - CHECK(asym_source_output_i[1 * 3 + 2] == doctest::Approx(0.0)); - CHECK(asym_sym_load_output_i[0] == doctest::Approx(test::i_load * 2 + test::i_shunt)); - CHECK(asym_asym_load_output_i[1] == doctest::Approx(0.0)); - CHECK(asym_shunt_output_i[2] == doctest::Approx(0.0)); - }; - - // update vector - std::vector sym_load_update_id{7}; - std::vector sym_load_update_status{1}; - std::vector sym_load_update_p_specified{2.5e6}; - - std::vector asym_load_update_id{8}; - std::vector asym_load_update_status{0}; - - std::vector shunt_update_id{9}; - std::vector shunt_update_status{0}; - std::vector shunt_update_b1{0.02}; - std::vector shunt_update_b0{0.02}; - - DatasetConst update_data{"update", 1, 1}; - update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); - update_data.add_attribute_buffer("sym_load", "id", sym_load_update_id.data()); - update_data.add_attribute_buffer("sym_load", "status", sym_load_update_status.data()); - update_data.add_attribute_buffer("sym_load", "p_specified", sym_load_update_p_specified.data()); - - update_data.add_buffer("asym_load", 1, 1, nullptr, nullptr); - update_data.add_attribute_buffer("asym_load", "id", asym_load_update_id.data()); - update_data.add_attribute_buffer("asym_load", "status", asym_load_update_status.data()); - - update_data.add_buffer("shunt", 1, 1, nullptr, nullptr); - update_data.add_attribute_buffer("shunt", "id", shunt_update_id.data()); - update_data.add_attribute_buffer("shunt", "status", shunt_update_status.data()); - update_data.add_attribute_buffer("shunt", "b1", shunt_update_b1.data()); - update_data.add_attribute_buffer("shunt", "b0", shunt_update_b0.data()); - - // This will lead to no topo change but param change - model.update(update_data); - - check_sym(); - check_asym(); - - SUBCASE("No new update") { - // Math state may be fully cached - } - SUBCASE("No new parameter change") { - // Math state may be fully cached - model.update(update_data); - } - - check_asym(); - check_sym(); -} - -namespace { -auto get_incomplete_state() -> State { - State result; - - // std::ranges::fill(result.source_status, 1); // TODO(mgovers): remove - std::ranges::fill(result.source_u_ref, nan); - std::ranges::fill(result.source_u_ref_angle, nan); - std::ranges::fill(result.sym_load_p_specified, nan); - std::ranges::fill(result.asym_load_p_specified, nan); - - return result; -} -} // namespace - -TEST_CASE("API model - incomplete input") { - State const complete_state; - State const incomplete_state = get_incomplete_state(); - - auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; - - for (auto symmetry : {PGM_symmetric, PGM_asymmetric}) { - CAPTURE(symmetry); - - auto const calculation_symmetry = symmetry == PGM_symmetric ? "Symmetric" : "Asymmetric"; - auto const output_type = symmetry == PGM_symmetric ? "sym_output" : "asym_output"; - - SUBCASE(calculation_symmetry) { - auto n_bytes = complete_state.node_id.size() * - MetaData::component_size(MetaData::get_component_by_name(output_type, "node")); - - std::vector test_sym_node(n_bytes); - DatasetMutable test_result_data{output_type, true, 1}; - test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, - test_sym_node.data()); - - SUBCASE("Target dataset") { - CHECK_THROWS_WITH_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), - doctest::Contains("Sparse matrix error, possibly singular matrix!"), - PowerGridRegularError); - } - SUBCASE("Empty single scenario update dataset") { - DatasetConst const empty_update_data{"update", true, 1}; - SUBCASE("Single update") { - test_model.update(empty_update_data); - CHECK_THROWS_WITH_AS( - test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), - doctest::Contains("Sparse matrix error, possibly singular matrix!"), PowerGridRegularError); - } - SUBCASE("Batch") { - CHECK_THROWS_WITH_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), - test_result_data, empty_update_data), - doctest::Contains("Sparse matrix error, possibly singular matrix!"), - PowerGridRegularError); - } - } - SUBCASE("Incomplete update dataset") { - DatasetConst incomplete_update_data{"update", true, 1}; - incomplete_update_data.add_buffer("source", incomplete_state.source_id.size(), - incomplete_state.source_id.size(), nullptr, nullptr); - incomplete_update_data.add_attribute_buffer("source", "id", incomplete_state.source_id.data()); - incomplete_update_data.add_attribute_buffer("source", "u_ref", incomplete_state.source_u_ref.data()); - incomplete_update_data.add_attribute_buffer("source", "u_ref_angle", - incomplete_state.source_u_ref_angle.data()); - - incomplete_update_data.add_buffer("sym_load", incomplete_state.sym_load_id.size(), - incomplete_state.sym_load_id.size(), nullptr, nullptr); - incomplete_update_data.add_attribute_buffer("sym_load", "id", incomplete_state.sym_load_id.data()); - incomplete_update_data.add_attribute_buffer("sym_load", "p_specified", - incomplete_state.sym_load_p_specified.data()); - - incomplete_update_data.add_buffer("asym_load", incomplete_state.asym_load_id.size(), - incomplete_state.asym_load_id.size(), nullptr, nullptr); - incomplete_update_data.add_attribute_buffer("asym_load", "id", incomplete_state.asym_load_id.data()); - incomplete_update_data.add_attribute_buffer("asym_load", "p_specified", - incomplete_state.asym_load_p_specified.data()); - - SUBCASE("Single update") { - CHECK_NOTHROW(test_model.update(incomplete_update_data)); - CHECK_THROWS_WITH_AS( - test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), - doctest::Contains("Sparse matrix error, possibly singular matrix!"), PowerGridRegularError); - } - SUBCASE("Batch") { - CHECK_THROWS_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data, - incomplete_update_data), - PowerGridBatchError); - } - } - SUBCASE("Complete update dataset") { - DatasetConst complete_update_data{"update", true, 1}; - complete_update_data.add_buffer("source", complete_state.source_id.size(), - complete_state.source_id.size(), nullptr, nullptr); - complete_update_data.add_attribute_buffer("source", "id", complete_state.source_id.data()); - complete_update_data.add_attribute_buffer("source", "u_ref", complete_state.source_u_ref.data()); - complete_update_data.add_attribute_buffer("source", "u_ref_angle", - complete_state.source_u_ref_angle.data()); - - complete_update_data.add_buffer("sym_load", complete_state.sym_load_id.size(), - complete_state.sym_load_id.size(), nullptr, nullptr); - complete_update_data.add_attribute_buffer("sym_load", "id", complete_state.sym_load_id.data()); - complete_update_data.add_attribute_buffer("sym_load", "p_specified", - complete_state.sym_load_p_specified.data()); - - complete_update_data.add_buffer("asym_load", complete_state.asym_load_id.size(), - complete_state.asym_load_id.size(), nullptr, nullptr); - complete_update_data.add_attribute_buffer("asym_load", "id", complete_state.asym_load_id.data()); - complete_update_data.add_attribute_buffer("asym_load", "p_specified", - complete_state.asym_load_p_specified.data()); - - auto ref_model = Model{50.0, complete_state.get_input_dataset()}; - std::vector ref_sym_node(n_bytes); - DatasetMutable ref_result_data{output_type, true, 1}; - ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), nullptr, - ref_sym_node.data()); - - CHECK_NOTHROW(ref_model.calculate(get_default_options(symmetry, PGM_linear), ref_result_data)); - - SUBCASE("Single calculation") { - test_model.update(complete_update_data); - CHECK_NOTHROW(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data, - complete_update_data)); - } - SUBCASE("Batch") { - CHECK_NOTHROW(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data, - complete_update_data)); - } - - CHECK(test_sym_node == ref_sym_node); - } - } - } -} - -TEST_CASE("API model - Incomplete scenario update followed by complete") { - State const complete_state; - State const incomplete_state = get_incomplete_state(); - - auto ref_model = Model{50.0, complete_state.get_input_dataset()}; - auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; - - constexpr Idx batch_size = 2; - auto const n_nodes = complete_state.node_id.size(); - - std::vector mixed_source_update_id{6, 10, 6, 10}; - std::vector mixed_source_update_status{1, 1, 1, 1}; - std::vector mixed_source_update_u_ref{nan, nan, 1.05, 1.05}; - std::vector mixed_source_update_u_ref_angle{nan, nan, nan, 0}; - - std::vector mixed_sym_load_update_id{7, 7}; - std::vector mixed_sym_load_update_status{1, 1}; - std::vector mixed_sym_load_update_p_specified{nan, 0.5e6}; - std::vector mixed_sym_load_update_q_specified{1.0, nan}; - - std::vector mixed_asym_load_update_id{8, 8}; - std::vector mixed_asym_load_update_status{1, 1}; - std::vector mixed_asym_load_update_p_specified{nan, nan, nan, 0.5e6 / 3.0, 0.5e6 / 3.0, 0.5e6 / 3.0}; - std::vector mixed_asym_load_update_q_specified{1.0, 1.0, 1.0, nan, nan, nan}; - - std::vector const source_indptr{0, 0, static_cast(mixed_source_update_id.size())}; - - REQUIRE(source_indptr.size() == batch_size + 1); - - DatasetConst mixed_update_data{"update", true, batch_size}; - - mixed_update_data.add_buffer("source", 2, 4, nullptr, nullptr); - mixed_update_data.add_attribute_buffer("source", "id", mixed_source_update_id.data()); - mixed_update_data.add_attribute_buffer("source", "status", mixed_source_update_status.data()); - mixed_update_data.add_attribute_buffer("source", "u_ref", mixed_source_update_u_ref.data()); - mixed_update_data.add_attribute_buffer("source", "u_ref_angle", mixed_source_update_u_ref_angle.data()); - - mixed_update_data.add_buffer("sym_load", 1, 2, nullptr, nullptr); - mixed_update_data.add_attribute_buffer("sym_load", "id", mixed_sym_load_update_id.data()); - mixed_update_data.add_attribute_buffer("sym_load", "status", mixed_sym_load_update_status.data()); - mixed_update_data.add_attribute_buffer("sym_load", "p_specified", mixed_sym_load_update_p_specified.data()); - mixed_update_data.add_attribute_buffer("sym_load", "q_specified", mixed_sym_load_update_q_specified.data()); - - mixed_update_data.add_buffer("asym_load", 1, 2, nullptr, nullptr); - mixed_update_data.add_attribute_buffer("asym_load", "id", mixed_asym_load_update_id.data()); - mixed_update_data.add_attribute_buffer("asym_load", "status", mixed_asym_load_update_status.data()); - mixed_update_data.add_attribute_buffer("asym_load", "p_specified", mixed_asym_load_update_p_specified.data()); - mixed_update_data.add_attribute_buffer("asym_load", "q_specified", mixed_asym_load_update_q_specified.data()); - - DatasetConst second_scenario_update_data{"update", true, 1}; - - second_scenario_update_data.add_buffer("source", 2, 2, nullptr, nullptr); - second_scenario_update_data.add_attribute_buffer("source", "id", mixed_source_update_id.data() + 2); - second_scenario_update_data.add_attribute_buffer("source", "status", mixed_source_update_status.data() + 2); - second_scenario_update_data.add_attribute_buffer("source", "u_ref", mixed_source_update_u_ref.data() + 2); - second_scenario_update_data.add_attribute_buffer("source", "u_ref_angle", - mixed_source_update_u_ref_angle.data() + 2); - - second_scenario_update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); - second_scenario_update_data.add_attribute_buffer("sym_load", "id", mixed_sym_load_update_id.data() + 1); - second_scenario_update_data.add_attribute_buffer("sym_load", "status", mixed_sym_load_update_status.data() + 1); - second_scenario_update_data.add_attribute_buffer("sym_load", "p_specified", - mixed_sym_load_update_p_specified.data() + 1); - second_scenario_update_data.add_attribute_buffer("sym_load", "q_specified", - mixed_sym_load_update_q_specified.data() + 1); - - second_scenario_update_data.add_buffer("asym_load", 1, 1, nullptr, nullptr); - second_scenario_update_data.add_attribute_buffer("asym_load", "id", mixed_asym_load_update_id.data() + 1); - second_scenario_update_data.add_attribute_buffer("asym_load", "status", mixed_asym_load_update_status.data() + 1); - second_scenario_update_data.add_attribute_buffer("asym_load", "p_specified", - mixed_asym_load_update_p_specified.data() + 1); - second_scenario_update_data.add_attribute_buffer("asym_load", "q_specified", - mixed_asym_load_update_q_specified.data() + 1); - - for (auto symmetry : {PGM_symmetric, PGM_asymmetric}) { - CAPTURE(symmetry); - - auto const calculation_symmetry = symmetry == PGM_symmetric ? "Symmetric" : "Asymmetric"; - auto const output_type = symmetry == PGM_symmetric ? "sym_output" : "asym_output"; - auto const n_phases = symmetry == PGM_symmetric ? 1 : 3; - - SUBCASE(calculation_symmetry) { - DatasetMutable test_result_data{output_type, true, batch_size}; - DatasetMutable ref_result_data{output_type, true, 1}; - - std::vector test_sym_node_u_pu(batch_size * n_nodes * n_phases, nan); - std::vector ref_sym_node_u_pu(n_nodes * n_phases, nan); - - test_result_data.add_buffer("node", n_nodes, batch_size * n_nodes, nullptr, nullptr); - test_result_data.add_attribute_buffer("node", "u_pu", test_sym_node_u_pu.data()); - - ref_result_data.add_buffer("node", n_nodes, n_nodes, nullptr, nullptr); - ref_result_data.add_attribute_buffer("node", "u_pu", ref_sym_node_u_pu.data()); - - CHECK_THROWS_AS(test_model.calculate(get_default_options(PGM_symmetric, PGM_linear), test_result_data, - mixed_update_data), - PowerGridBatchError); - - ref_model.calculate(get_default_options(PGM_symmetric, PGM_linear), ref_result_data, - second_scenario_update_data); - - for (auto node_idx = 0; node_idx < n_nodes; ++node_idx) { - CAPTURE(node_idx); - - for (auto phase_idx = 0; phase_idx < n_phases; ++phase_idx) { - CAPTURE(phase_idx); - - CHECK(is_nan(test_sym_node_u_pu[node_idx * n_phases + phase_idx])); - CHECK(test_sym_node_u_pu[(n_nodes + node_idx) * n_phases + phase_idx] == - doctest::Approx(ref_sym_node_u_pu[node_idx * n_phases + phase_idx])); - } - } - } - } -} - -} // namespace power_grid_model_cpp diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 7abc79ba6..abaccf29f 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -2,13 +2,19 @@ // // SPDX-License-Identifier: MPL-2.0 -#include "power_grid_model_cpp.hpp" - #include +#include +#include +#include #include +#include +#include +#include +#include #include +#include namespace { // Types for template parameters @@ -245,4 +251,647 @@ TEST_CASE_TEMPLATE( } } +namespace { +using std::numbers::pi; +using std::numbers::sqrt3; + +constexpr Idx default_option{-1}; +constexpr double deg_120 = 2.0 / 3.0 * pi; +constexpr double deg_240 = 4.0 / 3.0 * pi; + +enum class CalculationSymmetry : Idx { symmetric = PGM_symmetric, asymmetric = PGM_asymmetric }; +enum class LoadGenType : IntS { + const_pq = 0, // constant power + const_y = 1, // constant element_admittance (impedance) + const_i = 2, // constant current +}; +enum class MeasuredTerminalType : IntS { + branch_from = 0, + branch_to = 1, + source = 2, + shunt = 3, + load = 4, + generator = 5, + branch3_1 = 6, + branch3_2 = 7, + branch3_3 = 8, + node = 9 +}; + +Options get_default_options(PGM_SymmetryType calculation_symmetry, PGM_CalculationMethod calculation_method, + Idx threading = default_option) { + Options opt; + opt.set_calculation_type(PGM_power_flow); + opt.set_symmetric(calculation_symmetry); + opt.set_calculation_method(calculation_method); + if (threading != default_option) { + opt.set_threading(threading); + } + return opt; +} + +namespace test { +constexpr double z_bus_2 = 1.0 / (0.015 + 0.5e6 / 10e3 / 10e3 * 2); +constexpr double z_total = z_bus_2 + 10.0; +constexpr double u1 = 1.05 * z_bus_2 / (z_bus_2 + 10.0); +constexpr double i = 1.05 * 10e3 / z_total / sqrt3; +constexpr double i_shunt = 0.015 / 0.025 * i; +constexpr double i_load = 0.005 / 0.025 * i; +} // namespace test + +struct State { + std::vector node_id{1, 2, 3}; + std::vector node_u_rated{10e3, 10e3, 10e3}; + + std::vector line_id{4}; + std::vector line_from_node{1}; + std::vector line_to_node{2}; + std::vector line_from_status{1}; + std::vector line_to_status{1}; + std::vector line_r1{10.0}; + std::vector line_x1{0.0}; + std::vector line_c1{0.0}; + std::vector line_tan1{0.0}; + std::vector line_r0{10.0}; + std::vector line_x0{0.0}; + std::vector line_c0{0.0}; + std::vector line_tan0{0.0}; + std::vector line_i_n{1e3}; + + std::vector link_id{5}; + std::vector link_from_node{2}; + std::vector link_to_node{3}; + std::vector link_from_status{1}; + std::vector link_to_status{1}; + + std::vector source_id{6, 10}; + std::vector source_node{1, 3}; + std::vector source_status{1, 0}; + std::vector source_u_ref{1.05, 1.05}; + std::vector source_u_ref_angle{nan, 0.0}; + std::vector source_sk{1e12, 1e12}; + + std::vector sym_load_id{7}; + std::vector sym_load_node{3}; + std::vector sym_load_status{1}; + std::vector sym_load_type{LoadGenType::const_y}; + std::vector sym_load_p_specified{0.5e6}; + std::vector sym_load_q_specified{0.0}; + + std::vector asym_load_id{8}; + std::vector asym_load_node{3}; + std::vector asym_load_status{1}; + std::vector asym_load_type{LoadGenType::const_y}; + std::vector asym_load_p_specified{0.5e6 / 3.0, 0.5e6 / 3.0, 0.5e6 / 3.0}; + std::vector asym_load_q_specified{0.0, 0.0, 0.0}; + + std::vector shunt_id{9}; + std::vector shunt_node{3}; + std::vector shunt_status{1}; + std::vector shunt_g1{0.015}; + std::vector shunt_b1{0.0}; + std::vector shunt_g0{0.015}; + std::vector shunt_b0{0.0}; + + auto get_input_dataset() const { + DatasetConst result{"input", 0, 1}; + + result.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + result.add_attribute_buffer("node", "id", node_id.data()); + result.add_attribute_buffer("node", "u_rated", node_u_rated.data()); + + result.add_buffer("line", line_id.size(), line_id.size(), nullptr, nullptr); + result.add_attribute_buffer("line", "id", line_id.data()); + result.add_attribute_buffer("line", "from_node", line_from_node.data()); + result.add_attribute_buffer("line", "to_node", line_to_node.data()); + result.add_attribute_buffer("line", "from_status", line_from_status.data()); + result.add_attribute_buffer("line", "to_status", line_to_status.data()); + result.add_attribute_buffer("line", "r1", line_r1.data()); + result.add_attribute_buffer("line", "x1", line_x1.data()); + result.add_attribute_buffer("line", "c1", line_c1.data()); + result.add_attribute_buffer("line", "tan1", line_tan1.data()); + result.add_attribute_buffer("line", "r0", line_r0.data()); + result.add_attribute_buffer("line", "x0", line_x0.data()); + result.add_attribute_buffer("line", "c0", line_c0.data()); + result.add_attribute_buffer("line", "tan0", line_tan0.data()); + result.add_attribute_buffer("line", "i_n", line_i_n.data()); + + result.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); + result.add_attribute_buffer("link", "id", link_id.data()); + result.add_attribute_buffer("link", "from_node", link_from_node.data()); + result.add_attribute_buffer("link", "to_node", link_to_node.data()); + result.add_attribute_buffer("link", "from_status", link_from_status.data()); + result.add_attribute_buffer("link", "to_status", link_to_status.data()); + + result.add_buffer("source", source_id.size(), source_id.size(), nullptr, nullptr); + result.add_attribute_buffer("source", "id", source_id.data()); + result.add_attribute_buffer("source", "node", source_node.data()); + result.add_attribute_buffer("source", "status", source_status.data()); + result.add_attribute_buffer("source", "u_ref", source_u_ref.data()); + result.add_attribute_buffer("source", "u_ref_angle", source_u_ref_angle.data()); + result.add_attribute_buffer("source", "sk", source_sk.data()); + + result.add_buffer("sym_load", sym_load_id.size(), sym_load_id.size(), nullptr, nullptr); + result.add_attribute_buffer("sym_load", "id", sym_load_id.data()); + result.add_attribute_buffer("sym_load", "node", sym_load_node.data()); + result.add_attribute_buffer("sym_load", "status", sym_load_status.data()); + result.add_attribute_buffer("sym_load", "type", sym_load_type.data()); + result.add_attribute_buffer("sym_load", "p_specified", sym_load_p_specified.data()); + result.add_attribute_buffer("sym_load", "q_specified", sym_load_q_specified.data()); + + result.add_buffer("asym_load", asym_load_id.size(), asym_load_id.size(), nullptr, nullptr); + result.add_attribute_buffer("asym_load", "id", asym_load_id.data()); + result.add_attribute_buffer("asym_load", "node", asym_load_node.data()); + result.add_attribute_buffer("asym_load", "status", asym_load_status.data()); + result.add_attribute_buffer("asym_load", "type", asym_load_type.data()); + result.add_attribute_buffer("asym_load", "p_specified", asym_load_p_specified.data()); + result.add_attribute_buffer("asym_load", "q_specified", asym_load_q_specified.data()); + + result.add_buffer("shunt", shunt_id.size(), shunt_id.size(), nullptr, nullptr); + result.add_attribute_buffer("shunt", "id", shunt_id.data()); + result.add_attribute_buffer("shunt", "node", shunt_node.data()); + result.add_attribute_buffer("shunt", "status", shunt_status.data()); + result.add_attribute_buffer("shunt", "g1", shunt_g1.data()); + result.add_attribute_buffer("shunt", "b1", shunt_b1.data()); + result.add_attribute_buffer("shunt", "g0", shunt_g0.data()); + result.add_attribute_buffer("shunt", "b0", shunt_b0.data()); + + return result; + } +}; +} // namespace + +TEST_CASE("API model - all updates") { + using namespace std::string_literals; + + State state; + auto const input_dataset = state.get_input_dataset(); + auto const& input_info = input_dataset.get_info(); + auto model = Model{50.0, input_dataset}; + + // update vector + std::vector sym_load_update_id{7}; + std::vector sym_load_update_status{1}; + std::vector sym_load_update_p_specified{2.5e6}; + + std::vector asym_load_update_id{8}; + std::vector asym_load_update_status{0}; + + std::vector shunt_update_id{9}; + std::vector shunt_update_status{0}; + std::vector shunt_update_b1{0.02}; + std::vector shunt_update_b0{0.02}; + + // used for test case alternate compute mode + std::vector shunt_update_2_id{6}; + std::vector source_update_2_status{0}; + std::vector shunt_update_2_b1{0.01}; + std::vector shunt_update_2_b0{0.01}; + + std::vector source_update_id{10}; + std::vector source_update_status{1}; + std::vector source_update_u_ref{test::u1}; + + std::vector link_update_id{5}; + std::vector link_update_from_status{1}; + std::vector link_update_to_status{0}; + + DatasetConst update_data{"update", 1, 1}; + update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("sym_load", "id", sym_load_update_id.data()); + update_data.add_attribute_buffer("sym_load", "status", sym_load_update_status.data()); + update_data.add_attribute_buffer("sym_load", "p_specified", sym_load_update_p_specified.data()); + + update_data.add_buffer("asym_load", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("asym_load", "id", asym_load_update_id.data()); + update_data.add_attribute_buffer("asym_load", "status", asym_load_update_status.data()); + + update_data.add_buffer("shunt", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("shunt", "id", shunt_update_id.data()); + update_data.add_attribute_buffer("shunt", "status", shunt_update_status.data()); + update_data.add_attribute_buffer("shunt", "b1", shunt_update_b1.data()); + update_data.add_attribute_buffer("shunt", "b0", shunt_update_b0.data()); + + update_data.add_buffer("source", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("source", "id", source_update_id.data()); + update_data.add_attribute_buffer("source", "status", source_update_status.data()); + update_data.add_attribute_buffer("source", "u_ref", source_update_u_ref.data()); + + update_data.add_buffer("link", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("link", "id", link_update_id.data()); + update_data.add_attribute_buffer("link", "from_status", link_update_from_status.data()); + update_data.add_attribute_buffer("link", "to_status", link_update_to_status.data()); + + auto const output_dataset_type = "sym_output"s; + for (Idx comp_type_idx = 0; comp_type_idx < input_info.n_components(); ++comp_type_idx) { + CAPTURE(comp_type_idx); + + auto const comp_type = input_info.component_name(comp_type_idx); + CAPTURE(comp_type); + + auto const comp_meta = MetaData::get_component_by_name(output_dataset_type, comp_type); + auto const total_elements = input_info.component_total_elements(comp_type_idx); + auto const elements_per_scenario = input_info.component_elements_per_scenario(comp_type_idx); + auto const n_bytes = total_elements * MetaData::component_size(comp_meta); + + std::vector sym_output_from_batch(n_bytes); + std::vector sym_output_from_updated_single(n_bytes); + + DatasetMutable output_data_from_batch{output_dataset_type, 1, 1}; + DatasetMutable output_data_from_updated_single{output_dataset_type, false, 1}; + + output_data_from_batch.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, + sym_output_from_batch.data()); + output_data_from_updated_single.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, + sym_output_from_updated_single.data()); + + auto opt = get_default_options(PGM_symmetric, PGM_linear); + model.calculate(opt, output_data_from_batch, update_data); + model.update(update_data); + model.calculate(opt, output_data_from_updated_single); + + CHECK(sym_output_from_batch == sym_output_from_updated_single); + } +} + +TEST_CASE("API model - updates w/ alternating compute mode") { + State state; + auto const input_dataset = state.get_input_dataset(); + auto model = Model{50.0, input_dataset}; + + auto const check_sym = [&] { + std::vector sym_node_output_u_pu(3); + std::vector sym_line_output_i_from(1); + std::vector sym_source_output_i(2); + std::vector sym_sym_load_output_i(1); + std::vector sym_asym_load_output_i(1); + std::vector sym_shunt_output_i(1); + + DatasetMutable sym_output{"sym_output", false, 1}; + sym_output.add_buffer("node", 1, 1, nullptr, nullptr); + sym_output.add_attribute_buffer("node", "u_pu", sym_node_output_u_pu.data()); + + sym_output.add_buffer("line", 1, 1, nullptr, nullptr); + sym_output.add_attribute_buffer("line", "i_from", sym_line_output_i_from.data()); + + sym_output.add_buffer("source", 2, 2, nullptr, nullptr); + sym_output.add_attribute_buffer("source", "i", sym_source_output_i.data()); + + sym_output.add_buffer("sym_load", 1, 1, nullptr, nullptr); + sym_output.add_attribute_buffer("sym_load", "i", sym_sym_load_output_i.data()); + + sym_output.add_buffer("asym_load", 1, 1, nullptr, nullptr); + sym_output.add_attribute_buffer("asym_load", "i", sym_asym_load_output_i.data()); + + sym_output.add_buffer("shunt", 1, 1, nullptr, nullptr); + sym_output.add_attribute_buffer("shunt", "i", sym_shunt_output_i.data()); + + model.calculate(get_default_options(PGM_symmetric, PGM_linear), sym_output); + + CHECK(sym_node_output_u_pu[0] == doctest::Approx(1.05)); + CHECK(sym_node_output_u_pu[1] == doctest::Approx(test::u1)); + CHECK(sym_node_output_u_pu[2] == doctest::Approx(test::u1)); + CHECK(sym_line_output_i_from[0] == doctest::Approx(test::i)); + CHECK(sym_source_output_i[0] == doctest::Approx(test::i)); + CHECK(sym_source_output_i[1] == doctest::Approx(0.0)); + CHECK(sym_sym_load_output_i[0] == doctest::Approx(test::i_load * 2 + test::i_shunt)); + CHECK(sym_asym_load_output_i[0] == doctest::Approx(0.0)); + CHECK(sym_shunt_output_i[0] == doctest::Approx(0.0)); + }; + auto const check_asym = [&] { + std::vector asym_node_output_u_pu(3 * 3); + std::vector asym_line_output_i_from(1 * 3); + std::vector asym_source_output_i(2 * 3); + std::vector asym_sym_load_output_i(1 * 3); + std::vector asym_asym_load_output_i(1 * 3); + std::vector asym_shunt_output_i(1 * 3); + + DatasetMutable asym_output{"asym_output", false, 1}; + asym_output.add_buffer("node", 1, 1, nullptr, nullptr); + asym_output.add_attribute_buffer("node", "u_pu", asym_node_output_u_pu.data()); + + asym_output.add_buffer("line", 1, 1, nullptr, nullptr); + asym_output.add_attribute_buffer("line", "i_from", asym_line_output_i_from.data()); + + asym_output.add_buffer("source", 2, 2, nullptr, nullptr); + asym_output.add_attribute_buffer("source", "i", asym_source_output_i.data()); + + asym_output.add_buffer("sym_load", 1, 1, nullptr, nullptr); + asym_output.add_attribute_buffer("sym_load", "i", asym_sym_load_output_i.data()); + + asym_output.add_buffer("asym_load", 1, 1, nullptr, nullptr); + asym_output.add_attribute_buffer("asym_load", "i", asym_asym_load_output_i.data()); + + asym_output.add_buffer("shunt", 1, 1, nullptr, nullptr); + asym_output.add_attribute_buffer("shunt", "i", asym_shunt_output_i.data()); + + model.calculate(get_default_options(PGM_asymmetric, PGM_linear), asym_output); + + CHECK(asym_node_output_u_pu[0 * 3 + 0] == doctest::Approx(1.05)); + CHECK(asym_node_output_u_pu[1 * 3 + 1] == doctest::Approx(test::u1)); + CHECK(asym_node_output_u_pu[2 * 2 + 1] == doctest::Approx(test::u1)); + CHECK(asym_line_output_i_from[0] == doctest::Approx(test::i)); + CHECK(asym_source_output_i[0 * 3 + 1] == doctest::Approx(test::i)); + CHECK(asym_source_output_i[1 * 3 + 2] == doctest::Approx(0.0)); + CHECK(asym_sym_load_output_i[0] == doctest::Approx(test::i_load * 2 + test::i_shunt)); + CHECK(asym_asym_load_output_i[1] == doctest::Approx(0.0)); + CHECK(asym_shunt_output_i[2] == doctest::Approx(0.0)); + }; + + // update vector + std::vector sym_load_update_id{7}; + std::vector sym_load_update_status{1}; + std::vector sym_load_update_p_specified{2.5e6}; + + std::vector asym_load_update_id{8}; + std::vector asym_load_update_status{0}; + + std::vector shunt_update_id{9}; + std::vector shunt_update_status{0}; + std::vector shunt_update_b1{0.02}; + std::vector shunt_update_b0{0.02}; + + DatasetConst update_data{"update", 1, 1}; + update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("sym_load", "id", sym_load_update_id.data()); + update_data.add_attribute_buffer("sym_load", "status", sym_load_update_status.data()); + update_data.add_attribute_buffer("sym_load", "p_specified", sym_load_update_p_specified.data()); + + update_data.add_buffer("asym_load", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("asym_load", "id", asym_load_update_id.data()); + update_data.add_attribute_buffer("asym_load", "status", asym_load_update_status.data()); + + update_data.add_buffer("shunt", 1, 1, nullptr, nullptr); + update_data.add_attribute_buffer("shunt", "id", shunt_update_id.data()); + update_data.add_attribute_buffer("shunt", "status", shunt_update_status.data()); + update_data.add_attribute_buffer("shunt", "b1", shunt_update_b1.data()); + update_data.add_attribute_buffer("shunt", "b0", shunt_update_b0.data()); + + // This will lead to no topo change but param change + model.update(update_data); + + check_sym(); + check_asym(); + + SUBCASE("No new update") { + // Math state may be fully cached + } + SUBCASE("No new parameter change") { + // Math state may be fully cached + model.update(update_data); + } + + check_asym(); + check_sym(); +} + +namespace { +auto get_incomplete_state() -> State { + State result; + + // std::ranges::fill(result.source_status, 1); // TODO(mgovers): remove + std::ranges::fill(result.source_u_ref, nan); + std::ranges::fill(result.source_u_ref_angle, nan); + std::ranges::fill(result.sym_load_p_specified, nan); + std::ranges::fill(result.asym_load_p_specified, nan); + + return result; +} +} // namespace + +TEST_CASE("API model - incomplete input") { + State const complete_state; + State const incomplete_state = get_incomplete_state(); + + auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; + + for (auto symmetry : {PGM_symmetric, PGM_asymmetric}) { + CAPTURE(symmetry); + + auto const calculation_symmetry = symmetry == PGM_symmetric ? "Symmetric" : "Asymmetric"; + auto const output_type = symmetry == PGM_symmetric ? "sym_output" : "asym_output"; + + SUBCASE(calculation_symmetry) { + auto n_bytes = complete_state.node_id.size() * + MetaData::component_size(MetaData::get_component_by_name(output_type, "node")); + + std::vector test_sym_node(n_bytes); + DatasetMutable test_result_data{output_type, true, 1}; + test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, + test_sym_node.data()); + + SUBCASE("Target dataset") { + CHECK_THROWS_WITH_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), + doctest::Contains("Sparse matrix error, possibly singular matrix!"), + PowerGridRegularError); + } + SUBCASE("Empty single scenario update dataset") { + DatasetConst const empty_update_data{"update", true, 1}; + SUBCASE("Single update") { + test_model.update(empty_update_data); + CHECK_THROWS_WITH_AS( + test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), + doctest::Contains("Sparse matrix error, possibly singular matrix!"), PowerGridRegularError); + } + SUBCASE("Batch") { + CHECK_THROWS_WITH_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), + test_result_data, empty_update_data), + doctest::Contains("Sparse matrix error, possibly singular matrix!"), + PowerGridRegularError); + } + } + SUBCASE("Incomplete update dataset") { + DatasetConst incomplete_update_data{"update", true, 1}; + incomplete_update_data.add_buffer("source", incomplete_state.source_id.size(), + incomplete_state.source_id.size(), nullptr, nullptr); + incomplete_update_data.add_attribute_buffer("source", "id", incomplete_state.source_id.data()); + incomplete_update_data.add_attribute_buffer("source", "u_ref", incomplete_state.source_u_ref.data()); + incomplete_update_data.add_attribute_buffer("source", "u_ref_angle", + incomplete_state.source_u_ref_angle.data()); + + incomplete_update_data.add_buffer("sym_load", incomplete_state.sym_load_id.size(), + incomplete_state.sym_load_id.size(), nullptr, nullptr); + incomplete_update_data.add_attribute_buffer("sym_load", "id", incomplete_state.sym_load_id.data()); + incomplete_update_data.add_attribute_buffer("sym_load", "p_specified", + incomplete_state.sym_load_p_specified.data()); + + incomplete_update_data.add_buffer("asym_load", incomplete_state.asym_load_id.size(), + incomplete_state.asym_load_id.size(), nullptr, nullptr); + incomplete_update_data.add_attribute_buffer("asym_load", "id", incomplete_state.asym_load_id.data()); + incomplete_update_data.add_attribute_buffer("asym_load", "p_specified", + incomplete_state.asym_load_p_specified.data()); + + SUBCASE("Single update") { + CHECK_NOTHROW(test_model.update(incomplete_update_data)); + CHECK_THROWS_WITH_AS( + test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), + doctest::Contains("Sparse matrix error, possibly singular matrix!"), PowerGridRegularError); + } + SUBCASE("Batch") { + CHECK_THROWS_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data, + incomplete_update_data), + PowerGridBatchError); + } + } + SUBCASE("Complete update dataset") { + DatasetConst complete_update_data{"update", true, 1}; + complete_update_data.add_buffer("source", complete_state.source_id.size(), + complete_state.source_id.size(), nullptr, nullptr); + complete_update_data.add_attribute_buffer("source", "id", complete_state.source_id.data()); + complete_update_data.add_attribute_buffer("source", "u_ref", complete_state.source_u_ref.data()); + complete_update_data.add_attribute_buffer("source", "u_ref_angle", + complete_state.source_u_ref_angle.data()); + + complete_update_data.add_buffer("sym_load", complete_state.sym_load_id.size(), + complete_state.sym_load_id.size(), nullptr, nullptr); + complete_update_data.add_attribute_buffer("sym_load", "id", complete_state.sym_load_id.data()); + complete_update_data.add_attribute_buffer("sym_load", "p_specified", + complete_state.sym_load_p_specified.data()); + + complete_update_data.add_buffer("asym_load", complete_state.asym_load_id.size(), + complete_state.asym_load_id.size(), nullptr, nullptr); + complete_update_data.add_attribute_buffer("asym_load", "id", complete_state.asym_load_id.data()); + complete_update_data.add_attribute_buffer("asym_load", "p_specified", + complete_state.asym_load_p_specified.data()); + + auto ref_model = Model{50.0, complete_state.get_input_dataset()}; + std::vector ref_sym_node(n_bytes); + DatasetMutable ref_result_data{output_type, true, 1}; + ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), nullptr, + ref_sym_node.data()); + + CHECK_NOTHROW(ref_model.calculate(get_default_options(symmetry, PGM_linear), ref_result_data)); + + SUBCASE("Single calculation") { + test_model.update(complete_update_data); + CHECK_NOTHROW(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data, + complete_update_data)); + } + SUBCASE("Batch") { + CHECK_NOTHROW(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data, + complete_update_data)); + } + + CHECK(test_sym_node == ref_sym_node); + } + } + } +} + +TEST_CASE("API model - Incomplete scenario update followed by complete") { + State const complete_state; + State const incomplete_state = get_incomplete_state(); + + auto ref_model = Model{50.0, complete_state.get_input_dataset()}; + auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; + + constexpr Idx batch_size = 2; + auto const n_nodes = complete_state.node_id.size(); + + std::vector mixed_source_update_id{6, 10, 6, 10}; + std::vector mixed_source_update_status{1, 1, 1, 1}; + std::vector mixed_source_update_u_ref{nan, nan, 1.05, 1.05}; + std::vector mixed_source_update_u_ref_angle{nan, nan, nan, 0}; + + std::vector mixed_sym_load_update_id{7, 7}; + std::vector mixed_sym_load_update_status{1, 1}; + std::vector mixed_sym_load_update_p_specified{nan, 0.5e6}; + std::vector mixed_sym_load_update_q_specified{1.0, nan}; + + std::vector mixed_asym_load_update_id{8, 8}; + std::vector mixed_asym_load_update_status{1, 1}; + std::vector mixed_asym_load_update_p_specified{nan, nan, nan, 0.5e6 / 3.0, 0.5e6 / 3.0, 0.5e6 / 3.0}; + std::vector mixed_asym_load_update_q_specified{1.0, 1.0, 1.0, nan, nan, nan}; + + std::vector const source_indptr{0, 0, static_cast(mixed_source_update_id.size())}; + + REQUIRE(source_indptr.size() == batch_size + 1); + + DatasetConst mixed_update_data{"update", true, batch_size}; + + mixed_update_data.add_buffer("source", 2, 4, nullptr, nullptr); + mixed_update_data.add_attribute_buffer("source", "id", mixed_source_update_id.data()); + mixed_update_data.add_attribute_buffer("source", "status", mixed_source_update_status.data()); + mixed_update_data.add_attribute_buffer("source", "u_ref", mixed_source_update_u_ref.data()); + mixed_update_data.add_attribute_buffer("source", "u_ref_angle", mixed_source_update_u_ref_angle.data()); + + mixed_update_data.add_buffer("sym_load", 1, 2, nullptr, nullptr); + mixed_update_data.add_attribute_buffer("sym_load", "id", mixed_sym_load_update_id.data()); + mixed_update_data.add_attribute_buffer("sym_load", "status", mixed_sym_load_update_status.data()); + mixed_update_data.add_attribute_buffer("sym_load", "p_specified", mixed_sym_load_update_p_specified.data()); + mixed_update_data.add_attribute_buffer("sym_load", "q_specified", mixed_sym_load_update_q_specified.data()); + + mixed_update_data.add_buffer("asym_load", 1, 2, nullptr, nullptr); + mixed_update_data.add_attribute_buffer("asym_load", "id", mixed_asym_load_update_id.data()); + mixed_update_data.add_attribute_buffer("asym_load", "status", mixed_asym_load_update_status.data()); + mixed_update_data.add_attribute_buffer("asym_load", "p_specified", mixed_asym_load_update_p_specified.data()); + mixed_update_data.add_attribute_buffer("asym_load", "q_specified", mixed_asym_load_update_q_specified.data()); + + DatasetConst second_scenario_update_data{"update", true, 1}; + + second_scenario_update_data.add_buffer("source", 2, 2, nullptr, nullptr); + second_scenario_update_data.add_attribute_buffer("source", "id", mixed_source_update_id.data() + 2); + second_scenario_update_data.add_attribute_buffer("source", "status", mixed_source_update_status.data() + 2); + second_scenario_update_data.add_attribute_buffer("source", "u_ref", mixed_source_update_u_ref.data() + 2); + second_scenario_update_data.add_attribute_buffer("source", "u_ref_angle", + mixed_source_update_u_ref_angle.data() + 2); + + second_scenario_update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); + second_scenario_update_data.add_attribute_buffer("sym_load", "id", mixed_sym_load_update_id.data() + 1); + second_scenario_update_data.add_attribute_buffer("sym_load", "status", mixed_sym_load_update_status.data() + 1); + second_scenario_update_data.add_attribute_buffer("sym_load", "p_specified", + mixed_sym_load_update_p_specified.data() + 1); + second_scenario_update_data.add_attribute_buffer("sym_load", "q_specified", + mixed_sym_load_update_q_specified.data() + 1); + + second_scenario_update_data.add_buffer("asym_load", 1, 1, nullptr, nullptr); + second_scenario_update_data.add_attribute_buffer("asym_load", "id", mixed_asym_load_update_id.data() + 1); + second_scenario_update_data.add_attribute_buffer("asym_load", "status", mixed_asym_load_update_status.data() + 1); + second_scenario_update_data.add_attribute_buffer("asym_load", "p_specified", + mixed_asym_load_update_p_specified.data() + 1); + second_scenario_update_data.add_attribute_buffer("asym_load", "q_specified", + mixed_asym_load_update_q_specified.data() + 1); + + for (auto symmetry : {PGM_symmetric, PGM_asymmetric}) { + CAPTURE(symmetry); + + auto const calculation_symmetry = symmetry == PGM_symmetric ? "Symmetric" : "Asymmetric"; + auto const output_type = symmetry == PGM_symmetric ? "sym_output" : "asym_output"; + auto const n_phases = symmetry == PGM_symmetric ? 1 : 3; + + SUBCASE(calculation_symmetry) { + DatasetMutable test_result_data{output_type, true, batch_size}; + DatasetMutable ref_result_data{output_type, true, 1}; + + std::vector test_sym_node_u_pu(batch_size * n_nodes * n_phases, nan); + std::vector ref_sym_node_u_pu(n_nodes * n_phases, nan); + + test_result_data.add_buffer("node", n_nodes, batch_size * n_nodes, nullptr, nullptr); + test_result_data.add_attribute_buffer("node", "u_pu", test_sym_node_u_pu.data()); + + ref_result_data.add_buffer("node", n_nodes, n_nodes, nullptr, nullptr); + ref_result_data.add_attribute_buffer("node", "u_pu", ref_sym_node_u_pu.data()); + + CHECK_THROWS_AS(test_model.calculate(get_default_options(PGM_symmetric, PGM_linear), test_result_data, + mixed_update_data), + PowerGridBatchError); + + ref_model.calculate(get_default_options(PGM_symmetric, PGM_linear), ref_result_data, + second_scenario_update_data); + + for (auto node_idx = 0; node_idx < n_nodes; ++node_idx) { + CAPTURE(node_idx); + + for (auto phase_idx = 0; phase_idx < n_phases; ++phase_idx) { + CAPTURE(phase_idx); + + CHECK(is_nan(test_sym_node_u_pu[node_idx * n_phases + phase_idx])); + CHECK(test_sym_node_u_pu[(n_nodes + node_idx) * n_phases + phase_idx] == + doctest::Approx(ref_sym_node_u_pu[node_idx * n_phases + phase_idx])); + } + } + } + } +} + } // namespace power_grid_model_cpp From 7e1cb78181bcfaeef842f9e6828e889cbbdee4de Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 2 Dec 2024 16:41:22 +0100 Subject: [PATCH 21/49] remove todo Signed-off-by: Martijn Govers --- tests/native_api_tests/test_api_model_update.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index abaccf29f..4ee1edbea 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -649,7 +649,6 @@ namespace { auto get_incomplete_state() -> State { State result; - // std::ranges::fill(result.source_status, 1); // TODO(mgovers): remove std::ranges::fill(result.source_u_ref, nan); std::ranges::fill(result.source_u_ref_angle, nan); std::ranges::fill(result.sym_load_p_specified, nan); From a38fb54d89c4c77fd56bff119fb76b0960165b5f Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 08:29:27 +0100 Subject: [PATCH 22/49] fix error message when serializated file invalid Signed-off-by: Martijn Govers --- .../auxiliary/serialization/deserializer.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp index 00d02a04b..ba6cd8363 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp @@ -193,7 +193,7 @@ struct MapArrayVisitor : DefaultErrorVisitor> { static constexpr bool enable_array = std::same_as || std::same_as; static constexpr std::string_view static_err_msg = - enable_map ? (enable_array ? "Expect a map or array." : "Expect a map.") : "Expect an array."; + enable_map ? (enable_array ? "Expected a map or array." : "Expected a map.") : "Expected an array."; Idx size{}; bool is_map{}; @@ -226,7 +226,7 @@ struct MapArrayVisitor : DefaultErrorVisitor> { }; struct StringVisitor : DefaultErrorVisitor { - static constexpr std::string_view static_err_msg = "Expect a string."; + static constexpr std::string_view static_err_msg = "Expected a string."; std::string_view str{}; bool visit_str(const char* v, uint32_t size) { @@ -236,7 +236,7 @@ struct StringVisitor : DefaultErrorVisitor { }; struct BoolVisitor : DefaultErrorVisitor { - static constexpr std::string_view static_err_msg = "Expect a boolean."; + static constexpr std::string_view static_err_msg = "Expected a boolean."; bool value{}; bool visit_boolean(bool v) { @@ -248,7 +248,7 @@ struct BoolVisitor : DefaultErrorVisitor { template struct ValueVisitor; template struct ValueVisitor : DefaultErrorVisitor> { - static constexpr std::string_view static_err_msg = "Expect an interger."; + static constexpr std::string_view static_err_msg = "Expected an integer."; T& value; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) @@ -270,7 +270,7 @@ template struct ValueVisitor : DefaultErrorVisitor struct ValueVisitor : DefaultErrorVisitor> { - static constexpr std::string_view static_err_msg = "Expect a number."; + static constexpr std::string_view static_err_msg = "Expected a number."; double& value; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) @@ -294,7 +294,7 @@ template <> struct ValueVisitor : DefaultErrorVisitor struct ValueVisitor> : DefaultErrorVisitor>> { - static constexpr std::string_view static_err_msg = "Expect an array of 3 numbers."; + static constexpr std::string_view static_err_msg = "Expected an array of 3 numbers."; RealValue& value; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) Idx idx{}; From c897c03507a948e9bdcb84d64c5190535d85fe8f Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 08:38:20 +0100 Subject: [PATCH 23/49] fix clang tidy Signed-off-by: Martijn Govers --- tests/native_api_tests/test_api_model.cpp | 12 ++++++------ tests/native_api_tests/test_api_model_update.cpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index 64fb99dda..43554b778 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -848,7 +848,7 @@ TEST_CASE("API Model") { std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; DatasetConst input_dataset{"input", 0, 1}; - input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); input_dataset.add_attribute_buffer("node", "id", node_id.data()); input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); @@ -865,7 +865,7 @@ TEST_CASE("API Model") { std::vector node_id{1, 1, 3}; DatasetConst input_dataset{"input", 0, 1}; - input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); input_dataset.add_attribute_buffer("node", "id", node_id.data()); auto construct_model = [&] { Model{50.0, input_dataset}; }; @@ -882,11 +882,11 @@ TEST_CASE("API Model") { DatasetConst input_dataset{"input", 0, 1}; - input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); input_dataset.add_attribute_buffer("node", "id", node_id.data()); input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); - input_dataset.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); + input_dataset.add_buffer("link", std::ssize(link_id), std::ssize(link_id), nullptr, nullptr); input_dataset.add_attribute_buffer("link", "id", link_id.data()); input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); @@ -916,7 +916,7 @@ TEST_CASE("API Model") { DatasetConst input_dataset{"input", 0, 1}; - input_dataset.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); input_dataset.add_attribute_buffer("node", "id", node_id.data()); input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); @@ -925,7 +925,7 @@ TEST_CASE("API Model") { input_dataset.add_attribute_buffer("line", "from_node", line_from_node.data()); input_dataset.add_attribute_buffer("line", "to_node", line_to_node.data()); - input_dataset.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); + input_dataset.add_buffer("link", std::ssize(link_id), std::ssize(link_id), nullptr, nullptr); input_dataset.add_attribute_buffer("link", "id", link_id.data()); input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 4ee1edbea..8705f1b13 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -785,7 +785,7 @@ TEST_CASE("API model - Incomplete scenario update followed by complete") { auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; constexpr Idx batch_size = 2; - auto const n_nodes = complete_state.node_id.size(); + auto const n_nodes = static_cast(complete_state.node_id.size()); std::vector mixed_source_update_id{6, 10, 6, 10}; std::vector mixed_source_update_status{1, 1, 1, 1}; From 2d2d9b8e7319f3bcdb0dc57a62459b1017338457 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 08:54:15 +0100 Subject: [PATCH 24/49] more clang-tidy Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 8 +++++-- tests/native_api_tests/test_api_model.cpp | 12 +++++----- .../test_api_model_update.cpp | 22 +++++++------------ 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp index 5e405bbae..dacf51754 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp @@ -88,9 +88,11 @@ class DatasetWritable { class DatasetMutable { public: - DatasetMutable(std::string const& dataset, Idx is_batch, Idx batch_size) + explicit DatasetMutable(std::string const& dataset, Idx is_batch, Idx batch_size) : dataset_{handle_.call_with(PGM_create_dataset_mutable, dataset.c_str(), is_batch, batch_size)}, info_{handle_.call_with(PGM_dataset_mutable_get_info, get())} {} + explicit DatasetMutable(std::string const& dataset, std::same_as auto is_batch, Idx batch_size) + : DatasetMutable{dataset, is_batch ? Idx{1} : Idx{0}, batch_size} {} RawMutableDataset const* get() const { return dataset_.get(); } RawMutableDataset* get() { return dataset_.get(); } @@ -126,9 +128,11 @@ class DatasetMutable { class DatasetConst { public: - DatasetConst(std::string const& dataset, Idx is_batch, Idx batch_size) + explicit DatasetConst(std::string const& dataset, Idx is_batch, Idx batch_size) : dataset_{handle_.call_with(PGM_create_dataset_const, dataset.c_str(), is_batch, batch_size)}, info_{handle_.call_with(PGM_dataset_const_get_info, get())} {} + explicit DatasetConst(std::string const& dataset, std::same_as auto is_batch, Idx batch_size) + : DatasetConst{dataset, is_batch ? Idx{1} : Idx{0}, batch_size} {} DatasetConst(DatasetWritable const& writable_dataset) : dataset_{handle_.call_with(PGM_create_dataset_const_from_writable, writable_dataset.get())}, info_{handle_.call_with(PGM_dataset_const_get_info, get())} {} diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index 43554b778..2e722970b 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -857,7 +857,7 @@ TEST_CASE("API Model") { std::vector const ids_to_index{2, 1, 3, 2}; std::vector const expected_indexer{1, 0, 2, 1}; std::vector indexer(ids_to_index.size()); - model.get_indexer("node", ids_to_index.size(), ids_to_index.data(), indexer.data()); + model.get_indexer("node", std::ssize(ids_to_index), ids_to_index.data(), indexer.data()); CHECK(indexer == expected_indexer); } @@ -920,7 +920,7 @@ TEST_CASE("API Model") { input_dataset.add_attribute_buffer("node", "id", node_id.data()); input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); - input_dataset.add_buffer("line", line_id.size(), line_id.size(), nullptr, nullptr); + input_dataset.add_buffer("line", std::ssize(line_id), std::ssize(line_id), nullptr, nullptr); input_dataset.add_attribute_buffer("line", "id", line_id.data()); input_dataset.add_attribute_buffer("line", "from_node", line_from_node.data()); input_dataset.add_attribute_buffer("line", "to_node", line_to_node.data()); @@ -930,14 +930,14 @@ TEST_CASE("API Model") { input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); - input_dataset.add_buffer("sym_voltage_sensor", sym_voltage_sensor_id.size(), sym_voltage_sensor_id.size(), - nullptr, nullptr); + input_dataset.add_buffer("sym_voltage_sensor", std::ssize(sym_voltage_sensor_id), + std::ssize(sym_voltage_sensor_id), nullptr, nullptr); input_dataset.add_attribute_buffer("sym_voltage_sensor", "id", sym_voltage_sensor_id.data()); input_dataset.add_attribute_buffer("sym_voltage_sensor", "measured_object", sym_voltage_sensor_measured_object.data()); - input_dataset.add_buffer("sym_power_sensor", sym_power_sensor_id.size(), sym_power_sensor_id.size(), nullptr, - nullptr); + input_dataset.add_buffer("sym_power_sensor", std::ssize(sym_power_sensor_id), std::ssize(sym_power_sensor_id), + nullptr, nullptr); input_dataset.add_attribute_buffer("sym_power_sensor", "id", sym_power_sensor_id.data()); input_dataset.add_attribute_buffer("sym_power_sensor", "measured_object", sym_power_sensor_measured_object.data()); diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 8705f1b13..e26deba88 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -256,8 +256,6 @@ using std::numbers::pi; using std::numbers::sqrt3; constexpr Idx default_option{-1}; -constexpr double deg_120 = 2.0 / 3.0 * pi; -constexpr double deg_240 = 4.0 / 3.0 * pi; enum class CalculationSymmetry : Idx { symmetric = PGM_symmetric, asymmetric = PGM_asymmetric }; enum class LoadGenType : IntS { @@ -278,15 +276,11 @@ enum class MeasuredTerminalType : IntS { node = 9 }; -Options get_default_options(PGM_SymmetryType calculation_symmetry, PGM_CalculationMethod calculation_method, - Idx threading = default_option) { +Options get_default_options(PGM_SymmetryType calculation_symmetry, PGM_CalculationMethod calculation_method) { Options opt; opt.set_calculation_type(PGM_power_flow); opt.set_symmetric(calculation_symmetry); opt.set_calculation_method(calculation_method); - if (threading != default_option) { - opt.set_threading(threading); - } return opt; } @@ -356,11 +350,11 @@ struct State { auto get_input_dataset() const { DatasetConst result{"input", 0, 1}; - result.add_buffer("node", node_id.size(), node_id.size(), nullptr, nullptr); + result.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); result.add_attribute_buffer("node", "id", node_id.data()); result.add_attribute_buffer("node", "u_rated", node_u_rated.data()); - result.add_buffer("line", line_id.size(), line_id.size(), nullptr, nullptr); + result.add_buffer("line", std::ssize(line_id), std::ssize(line_id), nullptr, nullptr); result.add_attribute_buffer("line", "id", line_id.data()); result.add_attribute_buffer("line", "from_node", line_from_node.data()); result.add_attribute_buffer("line", "to_node", line_to_node.data()); @@ -376,14 +370,14 @@ struct State { result.add_attribute_buffer("line", "tan0", line_tan0.data()); result.add_attribute_buffer("line", "i_n", line_i_n.data()); - result.add_buffer("link", link_id.size(), link_id.size(), nullptr, nullptr); + result.add_buffer("link", std::ssize(link_id), std::ssize(link_id), nullptr, nullptr); result.add_attribute_buffer("link", "id", link_id.data()); result.add_attribute_buffer("link", "from_node", link_from_node.data()); result.add_attribute_buffer("link", "to_node", link_to_node.data()); result.add_attribute_buffer("link", "from_status", link_from_status.data()); result.add_attribute_buffer("link", "to_status", link_to_status.data()); - result.add_buffer("source", source_id.size(), source_id.size(), nullptr, nullptr); + result.add_buffer("source", std::ssize(source_id), std::ssize(source_id), nullptr, nullptr); result.add_attribute_buffer("source", "id", source_id.data()); result.add_attribute_buffer("source", "node", source_node.data()); result.add_attribute_buffer("source", "status", source_status.data()); @@ -391,7 +385,7 @@ struct State { result.add_attribute_buffer("source", "u_ref_angle", source_u_ref_angle.data()); result.add_attribute_buffer("source", "sk", source_sk.data()); - result.add_buffer("sym_load", sym_load_id.size(), sym_load_id.size(), nullptr, nullptr); + result.add_buffer("sym_load", std::ssize(sym_load_id), std::ssize(sym_load_id), nullptr, nullptr); result.add_attribute_buffer("sym_load", "id", sym_load_id.data()); result.add_attribute_buffer("sym_load", "node", sym_load_node.data()); result.add_attribute_buffer("sym_load", "status", sym_load_status.data()); @@ -399,7 +393,7 @@ struct State { result.add_attribute_buffer("sym_load", "p_specified", sym_load_p_specified.data()); result.add_attribute_buffer("sym_load", "q_specified", sym_load_q_specified.data()); - result.add_buffer("asym_load", asym_load_id.size(), asym_load_id.size(), nullptr, nullptr); + result.add_buffer("asym_load", std::ssize(asym_load_id), std::ssize(asym_load_id), nullptr, nullptr); result.add_attribute_buffer("asym_load", "id", asym_load_id.data()); result.add_attribute_buffer("asym_load", "node", asym_load_node.data()); result.add_attribute_buffer("asym_load", "status", asym_load_status.data()); @@ -407,7 +401,7 @@ struct State { result.add_attribute_buffer("asym_load", "p_specified", asym_load_p_specified.data()); result.add_attribute_buffer("asym_load", "q_specified", asym_load_q_specified.data()); - result.add_buffer("shunt", shunt_id.size(), shunt_id.size(), nullptr, nullptr); + result.add_buffer("shunt", std::ssize(shunt_id), std::ssize(shunt_id), nullptr, nullptr); result.add_attribute_buffer("shunt", "id", shunt_id.data()); result.add_attribute_buffer("shunt", "node", shunt_node.data()); result.add_attribute_buffer("shunt", "status", shunt_status.data()); From 7162012ba04fc59f471ced7516fa8eed98ac50f6 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 09:05:13 +0100 Subject: [PATCH 25/49] remove unused variable Signed-off-by: Martijn Govers --- tests/native_api_tests/test_api_model_update.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index e26deba88..03bb92e21 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -255,8 +255,6 @@ namespace { using std::numbers::pi; using std::numbers::sqrt3; -constexpr Idx default_option{-1}; - enum class CalculationSymmetry : Idx { symmetric = PGM_symmetric, asymmetric = PGM_asymmetric }; enum class LoadGenType : IntS { const_pq = 0, // constant power From 058a8972fda3a72f798b93a418e12f51dddb5eff Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 09:12:53 +0100 Subject: [PATCH 26/49] fix clang-tidy Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 12 ++++++++---- tests/native_api_tests/test_api_model_update.cpp | 14 ++++++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp index dacf51754..f7cac076c 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp @@ -88,8 +88,10 @@ class DatasetWritable { class DatasetMutable { public: - explicit DatasetMutable(std::string const& dataset, Idx is_batch, Idx batch_size) - : dataset_{handle_.call_with(PGM_create_dataset_mutable, dataset.c_str(), is_batch, batch_size)}, + DatasetMutable(std::string const& dataset, std::convertible_to auto is_batch, Idx batch_size) + requires(!std::same_as) + : handle_{}, + dataset_{handle_.call_with(PGM_create_dataset_mutable, dataset.c_str(), is_batch, batch_size)}, info_{handle_.call_with(PGM_dataset_mutable_get_info, get())} {} explicit DatasetMutable(std::string const& dataset, std::same_as auto is_batch, Idx batch_size) : DatasetMutable{dataset, is_batch ? Idx{1} : Idx{0}, batch_size} {} @@ -128,8 +130,10 @@ class DatasetMutable { class DatasetConst { public: - explicit DatasetConst(std::string const& dataset, Idx is_batch, Idx batch_size) - : dataset_{handle_.call_with(PGM_create_dataset_const, dataset.c_str(), is_batch, batch_size)}, + DatasetConst(std::string const& dataset, std::convertible_to auto is_batch, Idx batch_size) + requires(!std::same_as) + : handle_{}, + dataset_{handle_.call_with(PGM_create_dataset_const, dataset.c_str(), is_batch, batch_size)}, info_{handle_.call_with(PGM_dataset_const_get_info, get())} {} explicit DatasetConst(std::string const& dataset, std::same_as auto is_batch, Idx batch_size) : DatasetConst{dataset, is_batch ? Idx{1} : Idx{0}, batch_size} {} diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 03bb92e21..d6eb2719e 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -17,6 +17,8 @@ #include namespace { +using namespace std::string_literals; + // Types for template parameters struct row_t {}; struct columnar_t {}; @@ -659,10 +661,10 @@ TEST_CASE("API model - incomplete input") { for (auto symmetry : {PGM_symmetric, PGM_asymmetric}) { CAPTURE(symmetry); - auto const calculation_symmetry = symmetry == PGM_symmetric ? "Symmetric" : "Asymmetric"; - auto const output_type = symmetry == PGM_symmetric ? "sym_output" : "asym_output"; + auto const calculation_symmetry = symmetry == PGM_symmetric ? "Symmetric"s : "Asymmetric"s; + auto const output_type = symmetry == PGM_symmetric ? "sym_output"s : "asym_output"s; - SUBCASE(calculation_symmetry) { + SUBCASE(calculation_symmetry.c_str()) { auto n_bytes = complete_state.node_id.size() * MetaData::component_size(MetaData::get_component_by_name(output_type, "node")); @@ -846,11 +848,11 @@ TEST_CASE("API model - Incomplete scenario update followed by complete") { for (auto symmetry : {PGM_symmetric, PGM_asymmetric}) { CAPTURE(symmetry); - auto const calculation_symmetry = symmetry == PGM_symmetric ? "Symmetric" : "Asymmetric"; - auto const output_type = symmetry == PGM_symmetric ? "sym_output" : "asym_output"; + auto const calculation_symmetry = symmetry == PGM_symmetric ? "Symmetric"s : "Asymmetric"s; + auto const output_type = symmetry == PGM_symmetric ? "sym_output"s : "asym_output"s; auto const n_phases = symmetry == PGM_symmetric ? 1 : 3; - SUBCASE(calculation_symmetry) { + SUBCASE(calculation_symmetry.c_str()) { DatasetMutable test_result_data{output_type, true, batch_size}; DatasetMutable ref_result_data{output_type, true, 1}; From 95c0b4850751857ab56015e01513d9c26059a526 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 09:12:53 +0100 Subject: [PATCH 27/49] fix clang-tidy Signed-off-by: Martijn Govers --- .../test_api_model_update.cpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index d6eb2719e..5f67f5e1e 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -670,7 +670,7 @@ TEST_CASE("API model - incomplete input") { std::vector test_sym_node(n_bytes); DatasetMutable test_result_data{output_type, true, 1}; - test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, + test_result_data.add_buffer("node", std::ssize(test_sym_node), std::ssize(test_sym_node), nullptr, test_sym_node.data()); SUBCASE("Target dataset") { @@ -695,21 +695,21 @@ TEST_CASE("API model - incomplete input") { } SUBCASE("Incomplete update dataset") { DatasetConst incomplete_update_data{"update", true, 1}; - incomplete_update_data.add_buffer("source", incomplete_state.source_id.size(), - incomplete_state.source_id.size(), nullptr, nullptr); + incomplete_update_data.add_buffer("source", std::ssize(incomplete_state.source_id), + std::ssize(incomplete_state.source_id), nullptr, nullptr); incomplete_update_data.add_attribute_buffer("source", "id", incomplete_state.source_id.data()); incomplete_update_data.add_attribute_buffer("source", "u_ref", incomplete_state.source_u_ref.data()); incomplete_update_data.add_attribute_buffer("source", "u_ref_angle", incomplete_state.source_u_ref_angle.data()); - incomplete_update_data.add_buffer("sym_load", incomplete_state.sym_load_id.size(), - incomplete_state.sym_load_id.size(), nullptr, nullptr); + incomplete_update_data.add_buffer("sym_load", std::ssize(incomplete_state.sym_load_id), + std::ssize(incomplete_state.sym_load_id), nullptr, nullptr); incomplete_update_data.add_attribute_buffer("sym_load", "id", incomplete_state.sym_load_id.data()); incomplete_update_data.add_attribute_buffer("sym_load", "p_specified", incomplete_state.sym_load_p_specified.data()); - incomplete_update_data.add_buffer("asym_load", incomplete_state.asym_load_id.size(), - incomplete_state.asym_load_id.size(), nullptr, nullptr); + incomplete_update_data.add_buffer("asym_load", std::ssize(incomplete_state.asym_load_id), + std::ssize(incomplete_state.asym_load_id), nullptr, nullptr); incomplete_update_data.add_attribute_buffer("asym_load", "id", incomplete_state.asym_load_id.data()); incomplete_update_data.add_attribute_buffer("asym_load", "p_specified", incomplete_state.asym_load_p_specified.data()); @@ -728,21 +728,21 @@ TEST_CASE("API model - incomplete input") { } SUBCASE("Complete update dataset") { DatasetConst complete_update_data{"update", true, 1}; - complete_update_data.add_buffer("source", complete_state.source_id.size(), - complete_state.source_id.size(), nullptr, nullptr); + complete_update_data.add_buffer("source", std::ssize(complete_state.source_id), + std::ssize(complete_state.source_id), nullptr, nullptr); complete_update_data.add_attribute_buffer("source", "id", complete_state.source_id.data()); complete_update_data.add_attribute_buffer("source", "u_ref", complete_state.source_u_ref.data()); complete_update_data.add_attribute_buffer("source", "u_ref_angle", complete_state.source_u_ref_angle.data()); - complete_update_data.add_buffer("sym_load", complete_state.sym_load_id.size(), - complete_state.sym_load_id.size(), nullptr, nullptr); + complete_update_data.add_buffer("sym_load", std::ssize(complete_state.sym_load_id), + std::ssize(complete_state.sym_load_id), nullptr, nullptr); complete_update_data.add_attribute_buffer("sym_load", "id", complete_state.sym_load_id.data()); complete_update_data.add_attribute_buffer("sym_load", "p_specified", complete_state.sym_load_p_specified.data()); - complete_update_data.add_buffer("asym_load", complete_state.asym_load_id.size(), - complete_state.asym_load_id.size(), nullptr, nullptr); + complete_update_data.add_buffer("asym_load", std::ssize(complete_state.asym_load_id), + std::ssize(complete_state.asym_load_id), nullptr, nullptr); complete_update_data.add_attribute_buffer("asym_load", "id", complete_state.asym_load_id.data()); complete_update_data.add_attribute_buffer("asym_load", "p_specified", complete_state.asym_load_p_specified.data()); @@ -750,7 +750,7 @@ TEST_CASE("API model - incomplete input") { auto ref_model = Model{50.0, complete_state.get_input_dataset()}; std::vector ref_sym_node(n_bytes); DatasetMutable ref_result_data{output_type, true, 1}; - ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), nullptr, + ref_result_data.add_buffer("node", std::ssize(ref_sym_node), std::ssize(ref_sym_node), nullptr, ref_sym_node.data()); CHECK_NOTHROW(ref_model.calculate(get_default_options(symmetry, PGM_linear), ref_result_data)); From 4b8951e746b54c9a0a08cf5df80989bfdb716185 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 09:25:15 +0100 Subject: [PATCH 28/49] make even more explicit Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 4 +- tests/native_api_tests/test_api_model.cpp | 48 +++++++++---------- .../test_api_model_update.cpp | 14 +++--- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp index f7cac076c..a6bb12a4f 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp @@ -88,7 +88,7 @@ class DatasetWritable { class DatasetMutable { public: - DatasetMutable(std::string const& dataset, std::convertible_to auto is_batch, Idx batch_size) + DatasetMutable(std::string const& dataset, std::same_as auto is_batch, Idx batch_size) requires(!std::same_as) : handle_{}, dataset_{handle_.call_with(PGM_create_dataset_mutable, dataset.c_str(), is_batch, batch_size)}, @@ -130,7 +130,7 @@ class DatasetMutable { class DatasetConst { public: - DatasetConst(std::string const& dataset, std::convertible_to auto is_batch, Idx batch_size) + DatasetConst(std::string const& dataset, std::same_as auto is_batch, Idx batch_size) requires(!std::same_as) : handle_{}, dataset_{handle_.call_with(PGM_create_dataset_const, dataset.c_str(), is_batch, batch_size)}, diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index 2e722970b..474fdf35b 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -97,7 +97,7 @@ TEST_CASE("API Model") { Options options{}; // input data - DatasetConst input_dataset{"input", 0, 1}; + DatasetConst input_dataset{"input", false, 1}; // node buffer std::vector const node_id{0, 4}; @@ -164,11 +164,11 @@ TEST_CASE("API Model") { // output data Buffer node_output{PGM_def_sym_output_node, 2}; node_output.set_nan(); - DatasetMutable single_output_dataset{"sym_output", 0, 1}; + DatasetMutable single_output_dataset{"sym_output", false, 1}; single_output_dataset.add_buffer("node", 2, 2, nullptr, node_output); Buffer node_batch_output{PGM_def_sym_output_node, 4}; node_batch_output.set_nan(); - DatasetMutable batch_output_dataset{"sym_output", 1, 2}; + DatasetMutable batch_output_dataset{"sym_output", true, 2}; batch_output_dataset.add_buffer("node", 2, 4, nullptr, node_batch_output); std::vector node_result_id(2); @@ -205,14 +205,14 @@ TEST_CASE("API Model") { load_updates_buffer.set_value(PGM_def_update_sym_load_q_specified, load_updates_q_specified.data(), 0, -1); load_updates_buffer.set_value(PGM_def_update_sym_load_q_specified, load_updates_q_specified.data(), 1, -1); // dataset - DatasetConst single_update_dataset{"update", 0, 1}; + DatasetConst single_update_dataset{"update", false, 1}; single_update_dataset.add_buffer("source", 1, 1, nullptr, source_update_buffer); single_update_dataset.add_buffer("sym_load", 1, 1, nullptr, load_updates_buffer.get()); single_update_dataset.add_buffer("line", 2, 2, nullptr, nullptr); single_update_dataset.add_attribute_buffer("line", "id", line_id.data()); single_update_dataset.add_attribute_buffer("line", "from_status", line_from_status.data()); single_update_dataset.add_attribute_buffer("line", "to_status", line_to_status.data()); - DatasetConst batch_update_dataset{"update", 1, 2}; + DatasetConst batch_update_dataset{"update", true, 2}; batch_update_dataset.add_buffer("source", -1, 1, source_update_indptr.data(), source_update_buffer.get()); batch_update_dataset.add_buffer("sym_load", 1, 2, nullptr, load_updates_buffer); batch_update_dataset.add_buffer("line", 2, 4, nullptr, nullptr); @@ -343,7 +343,7 @@ TEST_CASE("API Model") { SUBCASE("Update error in calculation") { ID const bad_load_id = 2; load_buffer.set_value(PGM_def_input_sym_load_id, &bad_load_id, -1); - DatasetConst bad_batch_update_dataset{"update", 1, 2}; + DatasetConst bad_batch_update_dataset{"update", true, 2}; bad_batch_update_dataset.add_buffer("source", -1, 1, source_update_indptr.data(), source_update_buffer.get()); bad_batch_update_dataset.add_buffer("sym_load", 1, 2, nullptr, load_updates_buffer); @@ -477,13 +477,13 @@ TEST_CASE("API Model") { input_sym_load_buffer.set_value(PGM_def_input_sym_load_q_specified, input_sym_load_q_specified.data(), -1); // input dataset - row - DatasetConst input_dataset_row{"input", 0, 1}; + DatasetConst input_dataset_row{"input", false, 1}; input_dataset_row.add_buffer("node", 1, 1, nullptr, input_node_buffer); input_dataset_row.add_buffer("source", 1, 1, nullptr, input_source_buffer); input_dataset_row.add_buffer("sym_load", 1, 1, nullptr, input_sym_load_buffer); // input dataset - col - DatasetConst input_dataset_col{"input", 0, 1}; + DatasetConst input_dataset_col{"input", false, 1}; input_dataset_col.add_buffer("node", 1, 1, nullptr, nullptr); input_dataset_col.add_attribute_buffer("node", "id", input_node_id.data()); input_dataset_col.add_attribute_buffer("node", "u_rated", input_node_u_rated.data()); @@ -532,12 +532,12 @@ TEST_CASE("API Model") { -1); // update dataset - row - DatasetConst update_dataset_row{"update", 1, 2}; + DatasetConst update_dataset_row{"update", true, 2}; update_dataset_row.add_buffer("source", -1, 2, update_source_indptr.data(), update_source_buffer); update_dataset_row.add_buffer("sym_load", -1, 2, update_sym_load_indptr.data(), update_sym_load_buffer); // update dataset - col - DatasetConst update_dataset_col{"update", 1, 2}; + DatasetConst update_dataset_col{"update", true, 2}; update_dataset_col.add_buffer("source", -1, 2, update_source_indptr.data(), nullptr); update_dataset_col.add_attribute_buffer("source", "id", update_source_id.data()); @@ -548,13 +548,13 @@ TEST_CASE("API Model") { update_dataset_col.add_attribute_buffer("sym_load", "q_specified", update_sym_load_q_specified.data()); // update dataset - row no ids - DatasetConst update_dataset_row_no_id{"update", 1, 2}; + DatasetConst update_dataset_row_no_id{"update", true, 2}; update_dataset_row_no_id.add_buffer("source", -1, 2, update_source_indptr.data(), update_source_buffer_no_id); update_dataset_row_no_id.add_buffer("sym_load", -1, 2, update_sym_load_indptr.data(), update_sym_load_buffer_no_id); // update dataset - col no ids - DatasetConst update_dataset_col_no_id{"update", 1, 2}; + DatasetConst update_dataset_col_no_id{"update", true, 2}; update_dataset_col_no_id.add_buffer("source", -1, 2, update_source_indptr.data(), nullptr); update_dataset_col_no_id.add_attribute_buffer("source", "u_ref", update_source_u_ref.data()); @@ -565,7 +565,7 @@ TEST_CASE("API Model") { // output data Buffer batch_node_output{PGM_def_sym_output_node, 2}; batch_node_output.set_nan(); - DatasetMutable batch_output{"sym_output", 1, 2}; + DatasetMutable batch_output{"sym_output", true, 2}; batch_output.add_buffer("node", 1, 2, nullptr, batch_node_output); // options @@ -659,13 +659,13 @@ TEST_CASE("API Model") { input_sym_load_buffer.set_value(PGM_def_input_sym_load_q_specified, input_sym_load_q_specified.data(), -1); // input dataset - row - DatasetConst input_dataset_row{"input", 0, 1}; + DatasetConst input_dataset_row{"input", false, 1}; input_dataset_row.add_buffer("node", 1, 1, nullptr, input_node_buffer); input_dataset_row.add_buffer("source", 1, 1, nullptr, input_source_buffer); input_dataset_row.add_buffer("sym_load", 1, 1, nullptr, input_sym_load_buffer); // input dataset - col - DatasetConst input_dataset_col{"input", 0, 1}; + DatasetConst input_dataset_col{"input", false, 1}; input_dataset_col.add_buffer("node", 1, 1, nullptr, nullptr); input_dataset_col.add_attribute_buffer("node", "id", input_node_id.data()); input_dataset_col.add_attribute_buffer("node", "u_rated", input_node_u_rated.data()); @@ -704,12 +704,12 @@ TEST_CASE("API Model") { update_sym_load_buffer.set_value(PGM_def_update_sym_load_q_specified, update_sym_load_q_specified.data(), -1); // update dataset - row - DatasetConst update_dataset_row{"update", 1, 2}; + DatasetConst update_dataset_row{"update", true, 2}; update_dataset_row.add_buffer("source", -1, 1, source_indptr.data(), update_source_buffer); update_dataset_row.add_buffer("sym_load", -1, 2, sym_load_indptr.data(), update_sym_load_buffer); // update dataset - col - DatasetConst update_dataset_col{"update", 1, 2}; + DatasetConst update_dataset_col{"update", true, 2}; update_dataset_col.add_buffer("source", -1, 1, source_indptr.data(), nullptr); update_dataset_col.add_attribute_buffer("source", "id", update_source_id.data()); @@ -722,7 +722,7 @@ TEST_CASE("API Model") { // output data Buffer output_node_batch{PGM_def_sym_output_node, 2}; output_node_batch.set_nan(); - DatasetMutable output_batch_dataset{"sym_output", 1, 2}; + DatasetMutable output_batch_dataset{"sym_output", true, 2}; output_batch_dataset.add_buffer("node", 1, 2, nullptr, output_node_batch); // options @@ -769,7 +769,7 @@ TEST_CASE("API Model") { CAPTURE(calculation_type); auto const& supported_type_methods = supported_methods.at(calculation_type); - DatasetMutable const output_dataset{output_dataset_types.at(calculation_type), 0, 1}; + DatasetMutable const output_dataset{output_dataset_types.at(calculation_type), false, 1}; for (auto calculation_method : all_methods) { CAPTURE(calculation_method); @@ -792,7 +792,7 @@ TEST_CASE("API Model") { SUBCASE("Forbid link power measurements") { // input data - DatasetConst input_dataset_se{"input", 0, 1}; + DatasetConst input_dataset_se{"input", false, 1}; auto const construct_model = [&input_dataset_se] { return Model{50.0, input_dataset_se}; }; // node buffer @@ -847,7 +847,7 @@ TEST_CASE("API Model") { std::vector const node_id{1, 2, 3}; std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; - DatasetConst input_dataset{"input", 0, 1}; + DatasetConst input_dataset{"input", false, 1}; input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); input_dataset.add_attribute_buffer("node", "id", node_id.data()); input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); @@ -863,7 +863,7 @@ TEST_CASE("API Model") { SUBCASE("Test duplicated id") { std::vector node_id{1, 1, 3}; - DatasetConst input_dataset{"input", 0, 1}; + DatasetConst input_dataset{"input", false, 1}; input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); input_dataset.add_attribute_buffer("node", "id", node_id.data()); @@ -880,7 +880,7 @@ TEST_CASE("API Model") { std::vector link_from_node{99}; std::vector link_to_node{3}; - DatasetConst input_dataset{"input", 0, 1}; + DatasetConst input_dataset{"input", false, 1}; input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); input_dataset.add_attribute_buffer("node", "id", node_id.data()); @@ -914,7 +914,7 @@ TEST_CASE("API Model") { std::vector sym_power_sensor_measured_object{3}; std::vector sym_power_sensor_measured_terminal_type{MeasuredTerminalType::node}; - DatasetConst input_dataset{"input", 0, 1}; + DatasetConst input_dataset{"input", false, 1}; input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); input_dataset.add_attribute_buffer("node", "id", node_id.data()); diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 5f67f5e1e..17d75bb39 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -120,8 +120,8 @@ TEST_CASE_TEMPLATE( using sparsity_type = typename T::sparsity_type; using id_check_type = typename T::id_check_type; - DatasetConst input_dataset{"input", 0, 1}; - DatasetConst update_dataset{"update", 1, 2}; + DatasetConst input_dataset{"input", false, 1}; + DatasetConst update_dataset{"update", true, 2}; std::vector const node_id{0}; std::vector const node_u_rated{100.0}; @@ -230,7 +230,7 @@ TEST_CASE_TEMPLATE( // output dataset Buffer batch_node_output{PGM_def_sym_output_node, 2}; batch_node_output.set_nan(); - DatasetMutable batch_output_dataset{"sym_output", 1, 2}; + DatasetMutable batch_output_dataset{"sym_output", true, 2}; batch_output_dataset.add_buffer("node", 1, 2, nullptr, batch_node_output); Options const batch_options{}; @@ -348,7 +348,7 @@ struct State { std::vector shunt_b0{0.0}; auto get_input_dataset() const { - DatasetConst result{"input", 0, 1}; + DatasetConst result{"input", false, 1}; result.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); result.add_attribute_buffer("node", "id", node_id.data()); @@ -450,7 +450,7 @@ TEST_CASE("API model - all updates") { std::vector link_update_from_status{1}; std::vector link_update_to_status{0}; - DatasetConst update_data{"update", 1, 1}; + DatasetConst update_data{"update", true, 1}; update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); update_data.add_attribute_buffer("sym_load", "id", sym_load_update_id.data()); update_data.add_attribute_buffer("sym_load", "status", sym_load_update_status.data()); @@ -491,7 +491,7 @@ TEST_CASE("API model - all updates") { std::vector sym_output_from_batch(n_bytes); std::vector sym_output_from_updated_single(n_bytes); - DatasetMutable output_data_from_batch{output_dataset_type, 1, 1}; + DatasetMutable output_data_from_batch{output_dataset_type, true, 1}; DatasetMutable output_data_from_updated_single{output_dataset_type, false, 1}; output_data_from_batch.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, @@ -605,7 +605,7 @@ TEST_CASE("API model - updates w/ alternating compute mode") { std::vector shunt_update_b1{0.02}; std::vector shunt_update_b0{0.02}; - DatasetConst update_data{"update", 1, 1}; + DatasetConst update_data{"update", true, 1}; update_data.add_buffer("sym_load", 1, 1, nullptr, nullptr); update_data.add_attribute_buffer("sym_load", "id", sym_load_update_id.data()); update_data.add_attribute_buffer("sym_load", "status", sym_load_update_status.data()); From 29e8b68188e8460864c75d0e89f0b9b269b4f938 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 10:46:18 +0100 Subject: [PATCH 29/49] merge get indexer C API tests Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/buffer.hpp | 1 - tests/native_api_tests/test_api_model.cpp | 58 ++++++++++--------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/buffer.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/buffer.hpp index d326eac00..47bcc8156 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/buffer.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/buffer.hpp @@ -20,7 +20,6 @@ class Buffer { RawDataConstPtr get() const { return buffer_.get(); } RawDataPtr get() { return buffer_.get(); } - MetaComponent const* component() const { return component_; } Idx size() const { return size_; } void set_nan() { set_nan(0, size_); } diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index 474fdf35b..e9fb186aa 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -278,14 +278,38 @@ TEST_CASE("API Model") { } } - SUBCASE("Get indexer") { - std::array ids{2, 2}; - std::array indexer{3, 3}; - model.get_indexer("sym_load", 2, ids.data(), indexer.data()); - CHECK(indexer[0] == 0); - CHECK(indexer[1] == 0); - ids[1] = 6; - CHECK_THROWS_AS(model.get_indexer("sym_load", 2, ids.data(), indexer.data()), PowerGridRegularError); + SUBCASE("Test get indexer") { + std::vector const node_id{1, 2, 3}; + std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; + + DatasetConst input_dataset{"input", false, 1}; + input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); + input_dataset.add_attribute_buffer("node", "id", node_id.data()); + input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); + + auto model2 = Model{50.0, input_dataset}; + + SUBCASE("Good weather") { + std::vector const ids_to_index{2, 1, 3, 2}; + std::vector const expected_indexer{1, 0, 2, 1}; + std::vector indexer(ids_to_index.size()); + model2.get_indexer("node", std::ssize(ids_to_index), ids_to_index.data(), indexer.data()); + CHECK(indexer == expected_indexer); + } + SUBCASE("Bad weather: wrong id") { + std::vector const ids_to_index{2, 1, 3, 4}; + std::vector indexer(ids_to_index.size()); + CHECK_THROWS_WITH_AS( + model2.get_indexer("node", std::ssize(ids_to_index), ids_to_index.data(), indexer.data()), + doctest::Contains("The id cannot be found: 4"), PowerGridRegularError); + } + SUBCASE("Bad weather: wrong type") { + std::vector const ids_to_index{2, 1, 3, 2}; + std::vector indexer(ids_to_index.size()); + CHECK_THROWS_WITH_AS( + model2.get_indexer("sym_load", std::ssize(ids_to_index), ids_to_index.data(), indexer.data()), + doctest::Contains("Wrong type for object with id 2"), PowerGridRegularError); + } } SUBCASE("Batch power flow") { @@ -843,24 +867,6 @@ TEST_CASE("API Model") { } } - SUBCASE("Test get indexer") { - std::vector const node_id{1, 2, 3}; - std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; - - DatasetConst input_dataset{"input", false, 1}; - input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); - input_dataset.add_attribute_buffer("node", "id", node_id.data()); - input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); - - auto model = Model{50.0, input_dataset}; - - std::vector const ids_to_index{2, 1, 3, 2}; - std::vector const expected_indexer{1, 0, 2, 1}; - std::vector indexer(ids_to_index.size()); - model.get_indexer("node", std::ssize(ids_to_index), ids_to_index.data(), indexer.data()); - CHECK(indexer == expected_indexer); - } - SUBCASE("Test duplicated id") { std::vector node_id{1, 1, 3}; DatasetConst input_dataset{"input", false, 1}; From bacf4f0eed78b557adc7f148f1e11dd5f1a3ab79 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 11:27:22 +0100 Subject: [PATCH 30/49] use C++17 features in C++ wrapper Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp index a6bb12a4f..596491631 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp @@ -88,13 +88,20 @@ class DatasetWritable { class DatasetMutable { public: - DatasetMutable(std::string const& dataset, std::same_as auto is_batch, Idx batch_size) - requires(!std::same_as) + template || std::is_same_v>> + DatasetMutable(std::string const& dataset, T is_batch, Idx batch_size) : handle_{}, - dataset_{handle_.call_with(PGM_create_dataset_mutable, dataset.c_str(), is_batch, batch_size)}, + dataset_{handle_.call_with( + PGM_create_dataset_mutable, dataset.c_str(), + [is_batch]() -> Idx { + if constexpr (std::is_same_v) { + return is_batch ? Idx{1} : Idx{0}; + } else { + return is_batch; + } + }(), + batch_size)}, info_{handle_.call_with(PGM_dataset_mutable_get_info, get())} {} - explicit DatasetMutable(std::string const& dataset, std::same_as auto is_batch, Idx batch_size) - : DatasetMutable{dataset, is_batch ? Idx{1} : Idx{0}, batch_size} {} RawMutableDataset const* get() const { return dataset_.get(); } RawMutableDataset* get() { return dataset_.get(); } @@ -130,16 +137,25 @@ class DatasetMutable { class DatasetConst { public: - DatasetConst(std::string const& dataset, std::same_as auto is_batch, Idx batch_size) - requires(!std::same_as) + template || std::is_same_v>> + DatasetConst(std::string const& dataset, T is_batch, Idx batch_size) : handle_{}, - dataset_{handle_.call_with(PGM_create_dataset_const, dataset.c_str(), is_batch, batch_size)}, + dataset_{handle_.call_with( + PGM_create_dataset_const, dataset.c_str(), + [is_batch]() -> Idx { + if constexpr (std::is_same_v) { + return is_batch ? Idx{1} : Idx{0}; + } else { + return is_batch; + } + }(), + batch_size)}, info_{handle_.call_with(PGM_dataset_const_get_info, get())} {} - explicit DatasetConst(std::string const& dataset, std::same_as auto is_batch, Idx batch_size) - : DatasetConst{dataset, is_batch ? Idx{1} : Idx{0}, batch_size} {} + DatasetConst(DatasetWritable const& writable_dataset) : dataset_{handle_.call_with(PGM_create_dataset_const_from_writable, writable_dataset.get())}, info_{handle_.call_with(PGM_dataset_const_get_info, get())} {} + DatasetConst(DatasetMutable const& mutable_dataset) : dataset_{handle_.call_with(PGM_create_dataset_const_from_mutable, mutable_dataset.get())}, info_{handle_.call_with(PGM_dataset_const_get_info, get())} {} From 6309aa5831a891c339c3f5df0dbf409e7c0f8f90 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 11:30:08 +0100 Subject: [PATCH 31/49] calng-tidy Signed-off-by: Martijn Govers --- .../test_api_model_update.cpp | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 17d75bb39..cc1232486 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -254,7 +254,6 @@ TEST_CASE_TEMPLATE( } namespace { -using std::numbers::pi; using std::numbers::sqrt3; enum class CalculationSymmetry : Idx { symmetric = PGM_symmetric, asymmetric = PGM_asymmetric }; @@ -418,7 +417,7 @@ struct State { TEST_CASE("API model - all updates") { using namespace std::string_literals; - State state; + State const state; auto const input_dataset = state.get_input_dataset(); auto const& input_info = input_dataset.get_info(); auto model = Model{50.0, input_dataset}; @@ -437,10 +436,10 @@ TEST_CASE("API model - all updates") { std::vector shunt_update_b0{0.02}; // used for test case alternate compute mode - std::vector shunt_update_2_id{6}; - std::vector source_update_2_status{0}; - std::vector shunt_update_2_b1{0.01}; - std::vector shunt_update_2_b0{0.01}; + std::vector const shunt_update_2_id{6}; + std::vector const source_update_2_status{0}; + std::vector const shunt_update_2_b1{0.01}; + std::vector const shunt_update_2_b0{0.01}; std::vector source_update_id{10}; std::vector source_update_status{1}; @@ -483,7 +482,7 @@ TEST_CASE("API model - all updates") { auto const comp_type = input_info.component_name(comp_type_idx); CAPTURE(comp_type); - auto const comp_meta = MetaData::get_component_by_name(output_dataset_type, comp_type); + auto const* comp_meta = MetaData::get_component_by_name(output_dataset_type, comp_type); auto const total_elements = input_info.component_total_elements(comp_type_idx); auto const elements_per_scenario = input_info.component_elements_per_scenario(comp_type_idx); auto const n_bytes = total_elements * MetaData::component_size(comp_meta); @@ -553,12 +552,12 @@ TEST_CASE("API model - updates w/ alternating compute mode") { CHECK(sym_shunt_output_i[0] == doctest::Approx(0.0)); }; auto const check_asym = [&] { - std::vector asym_node_output_u_pu(3 * 3); - std::vector asym_line_output_i_from(1 * 3); - std::vector asym_source_output_i(2 * 3); - std::vector asym_sym_load_output_i(1 * 3); - std::vector asym_asym_load_output_i(1 * 3); - std::vector asym_shunt_output_i(1 * 3); + std::vector asym_node_output_u_pu(9); + std::vector asym_line_output_i_from(3); + std::vector asym_source_output_i(6); + std::vector asym_sym_load_output_i(3); + std::vector asym_asym_load_output_i(3); + std::vector asym_shunt_output_i(3); DatasetMutable asym_output{"asym_output", false, 1}; asym_output.add_buffer("node", 1, 1, nullptr, nullptr); From 7e567338c0c84aab65af292205e7aa9ef793c005 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 11:44:11 +0100 Subject: [PATCH 32/49] tests per attribute Signed-off-by: Martijn Govers --- .../test_api_model_update.cpp | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index cc1232486..704df1592 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -485,25 +485,39 @@ TEST_CASE("API model - all updates") { auto const* comp_meta = MetaData::get_component_by_name(output_dataset_type, comp_type); auto const total_elements = input_info.component_total_elements(comp_type_idx); auto const elements_per_scenario = input_info.component_elements_per_scenario(comp_type_idx); - auto const n_bytes = total_elements * MetaData::component_size(comp_meta); - std::vector sym_output_from_batch(n_bytes); - std::vector sym_output_from_updated_single(n_bytes); + for (Idx attribute_idx = 0; attribute_idx < MetaData::n_attributes(comp_meta); ++attribute_idx) { + CAPTURE(attribute_idx); + auto const attr_meta = MetaData::get_attribute_by_idx(comp_meta, attribute_idx); + auto const attribute_name = MetaData::attribute_name(attr_meta); + CAPTURE(attribute_name); - DatasetMutable output_data_from_batch{output_dataset_type, true, 1}; - DatasetMutable output_data_from_updated_single{output_dataset_type, false, 1}; + pgm_type_func_selector(attr_meta, [&]() { + std::vector sym_output_from_batch(total_elements); + std::vector sym_output_from_updated_single(total_elements); - output_data_from_batch.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, - sym_output_from_batch.data()); - output_data_from_updated_single.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, - sym_output_from_updated_single.data()); + DatasetMutable output_data_from_batch{output_dataset_type, true, 1}; + DatasetMutable output_data_from_updated_single{output_dataset_type, false, 1}; - auto opt = get_default_options(PGM_symmetric, PGM_linear); - model.calculate(opt, output_data_from_batch, update_data); - model.update(update_data); - model.calculate(opt, output_data_from_updated_single); + output_data_from_batch.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, nullptr); + output_data_from_updated_single.add_buffer(comp_type, elements_per_scenario, total_elements, nullptr, + nullptr); + + output_data_from_batch.add_attribute_buffer(comp_type, attribute_name, sym_output_from_batch.data()); + output_data_from_updated_single.add_attribute_buffer(comp_type, attribute_name, + sym_output_from_updated_single.data()); + + auto opt = get_default_options(PGM_symmetric, PGM_linear); + model.calculate(opt, output_data_from_batch, update_data); + model.update(update_data); + model.calculate(opt, output_data_from_updated_single); - CHECK(sym_output_from_batch == sym_output_from_updated_single); + for (Idx i = 0; i < total_elements; ++i) { + CAPTURE(i); + CHECK(sym_output_from_batch[i] == sym_output_from_updated_single[i]); + } + }); + } } } From c69b8d8b0a7209280f13f3eedc5f6575adba5855 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 12:39:18 +0100 Subject: [PATCH 33/49] debug macos release Signed-off-by: Martijn Govers --- .../test_api_model_update.cpp | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 704df1592..b909dfd6e 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -681,10 +682,10 @@ TEST_CASE("API model - incomplete input") { auto n_bytes = complete_state.node_id.size() * MetaData::component_size(MetaData::get_component_by_name(output_type, "node")); - std::vector test_sym_node(n_bytes); + std::vector test_node_output(n_bytes, 0); DatasetMutable test_result_data{output_type, true, 1}; - test_result_data.add_buffer("node", std::ssize(test_sym_node), std::ssize(test_sym_node), nullptr, - test_sym_node.data()); + test_result_data.add_buffer("node", std::ssize(test_node_output), std::ssize(test_node_output), nullptr, + test_node_output.data()); SUBCASE("Target dataset") { CHECK_THROWS_WITH_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), @@ -761,10 +762,10 @@ TEST_CASE("API model - incomplete input") { complete_state.asym_load_p_specified.data()); auto ref_model = Model{50.0, complete_state.get_input_dataset()}; - std::vector ref_sym_node(n_bytes); + std::vector ref_node_output(n_bytes, 0); DatasetMutable ref_result_data{output_type, true, 1}; - ref_result_data.add_buffer("node", std::ssize(ref_sym_node), std::ssize(ref_sym_node), nullptr, - ref_sym_node.data()); + ref_result_data.add_buffer("node", std::ssize(ref_node_output), std::ssize(ref_node_output), nullptr, + ref_node_output.data()); CHECK_NOTHROW(ref_model.calculate(get_default_options(symmetry, PGM_linear), ref_result_data)); @@ -778,7 +779,12 @@ TEST_CASE("API model - incomplete input") { complete_update_data)); } - CHECK(test_sym_node == ref_sym_node); + CHECK(test_node_output == ref_node_output); + std::string_view test_node_output_str{test_node_output.data(), test_node_output.size()}; + std::string_view ref_node_output_str{ref_node_output.data(), ref_node_output.size()}; + CHECK(test_node_output_str == ref_node_output_str); + std::cout << "test: " << test_node_output_str << std::endl; + std::cout << "ref : " << ref_node_output_str << std::endl; } } } @@ -869,14 +875,14 @@ TEST_CASE("API model - Incomplete scenario update followed by complete") { DatasetMutable test_result_data{output_type, true, batch_size}; DatasetMutable ref_result_data{output_type, true, 1}; - std::vector test_sym_node_u_pu(batch_size * n_nodes * n_phases, nan); - std::vector ref_sym_node_u_pu(n_nodes * n_phases, nan); + std::vector test_node_output_u_pu(batch_size * n_nodes * n_phases, nan); + std::vector ref_node_output_u_pu(n_nodes * n_phases, nan); test_result_data.add_buffer("node", n_nodes, batch_size * n_nodes, nullptr, nullptr); - test_result_data.add_attribute_buffer("node", "u_pu", test_sym_node_u_pu.data()); + test_result_data.add_attribute_buffer("node", "u_pu", test_node_output_u_pu.data()); ref_result_data.add_buffer("node", n_nodes, n_nodes, nullptr, nullptr); - ref_result_data.add_attribute_buffer("node", "u_pu", ref_sym_node_u_pu.data()); + ref_result_data.add_attribute_buffer("node", "u_pu", ref_node_output_u_pu.data()); CHECK_THROWS_AS(test_model.calculate(get_default_options(PGM_symmetric, PGM_linear), test_result_data, mixed_update_data), @@ -891,9 +897,9 @@ TEST_CASE("API model - Incomplete scenario update followed by complete") { for (auto phase_idx = 0; phase_idx < n_phases; ++phase_idx) { CAPTURE(phase_idx); - CHECK(is_nan(test_sym_node_u_pu[node_idx * n_phases + phase_idx])); - CHECK(test_sym_node_u_pu[(n_nodes + node_idx) * n_phases + phase_idx] == - doctest::Approx(ref_sym_node_u_pu[node_idx * n_phases + phase_idx])); + CHECK(is_nan(test_node_output_u_pu[node_idx * n_phases + phase_idx])); + CHECK(test_node_output_u_pu[(n_nodes + node_idx) * n_phases + phase_idx] == + doctest::Approx(ref_node_output_u_pu[node_idx * n_phases + phase_idx])); } } } From 566ff3f89c26861d416659fa41c1f92ae0f18d6c Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 12:47:35 +0100 Subject: [PATCH 34/49] debug macos Signed-off-by: Martijn Govers --- .../test_api_model_update.cpp | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index b909dfd6e..96b2e8c6f 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -780,11 +780,26 @@ TEST_CASE("API model - incomplete input") { } CHECK(test_node_output == ref_node_output); - std::string_view test_node_output_str{test_node_output.data(), test_node_output.size()}; - std::string_view ref_node_output_str{ref_node_output.data(), ref_node_output.size()}; - CHECK(test_node_output_str == ref_node_output_str); - std::cout << "test: " << test_node_output_str << std::endl; - std::cout << "ref : " << ref_node_output_str << std::endl; + std::vector test_node_output_values(n_bytes, 0); + std::vector ref_node_output_values(n_bytes, 0); + + std::ranges::transform(test_node_output, test_node_output_values.begin(), + [](char c) { return static_cast(c); }); + std::ranges::transform(ref_node_output, ref_node_output_values.begin(), + [](char c) { return static_cast(c); }); + + CHECK(test_node_output_values == ref_node_output_values); + std::cout << "test: "; + for (auto i : test_node_output) { + std::cout << static_cast(i) << " "; + } + std::cout << "\n"; + + std::cout << "test: "; + for (auto i : ref_node_output) { + std::cout << static_cast(i) << " "; + } + std::cout << "\n\n"; } } } From 8f5862f9d883e23a75e3785ed2bdf190367becf3 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 15:35:35 +0100 Subject: [PATCH 35/49] sonar-cloud + clang-tidy Signed-off-by: Martijn Govers --- build.sh | 1 - docs/advanced_documentation/build-guide.md | 1 - sonar-project.properties | 2 +- .../test_api_model_update.cpp | 61 ++++++++++--------- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/build.sh b/build.sh index d87355f57..d09f26189 100755 --- a/build.sh +++ b/build.sh @@ -68,7 +68,6 @@ if [[ "${COVERAGE}" ]]; then PATH=${PATH}:${PWD} lcov -q -c \ -d ${BUILD_DIR}/tests/cpp_unit_tests/CMakeFiles/power_grid_model_unit_tests.dir \ - -d ${BUILD_DIR}/tests/cpp_integration_tests/CMakeFiles/power_grid_model_integration_tests.dir \ -d ${BUILD_DIR}/tests/cpp_validation_tests/CMakeFiles/power_grid_model_validation_tests.dir \ -d ${BUILD_DIR}/tests/native_api_tests/CMakeFiles/power_grid_model_api_tests.dir \ -d ${BUILD_DIR}/power_grid_model_c/power_grid_model_c/CMakeFiles/power_grid_model_c.dir \ diff --git a/docs/advanced_documentation/build-guide.md b/docs/advanced_documentation/build-guide.md index 6b2ca2dbf..04975c418 100644 --- a/docs/advanced_documentation/build-guide.md +++ b/docs/advanced_documentation/build-guide.md @@ -161,7 +161,6 @@ In the developer build the following build targets (directories) are enabled: * `power_grid_model_c`: a dynamic library (`.dll` or `.so`) with stable pure C API/ABI which can be used by any application * `tests/cpp_unit_tests`: the unit test target for the C++ core using the `doctest` framework. -* `tests/cpp_integration_tests`: the integration test target for the C++ core using the `doctest` framework. * `tests/cpp_validation_tests`: the validation test target using the `doctest` framework * `tests/native_api_tests`: the C API test target using the `doctest` framework * `tests/benchmark_cpp`: the C++ benchmark target for performance measure. diff --git a/sonar-project.properties b/sonar-project.properties index 7d3b10ac1..a6b51be2b 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -11,7 +11,7 @@ sonar.projectVersion=1.0 # include C++ and Python source file # since the C++ part is header only, also include the C++ unit test .cpp file -sonar.sources=src,tests/cpp_unit_tests,tests/cpp_integration_tests,tests/cpp_validation_tests,tests/native_api_tests,power_grid_model_c +sonar.sources=src,tests/cpp_unit_tests,tests/cpp_validation_tests,tests/native_api_tests,power_grid_model_c sonar.tests=tests/unit sonar.sourceEncoding=UTF-8 diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 96b2e8c6f..abb587fcc 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -489,7 +489,7 @@ TEST_CASE("API model - all updates") { for (Idx attribute_idx = 0; attribute_idx < MetaData::n_attributes(comp_meta); ++attribute_idx) { CAPTURE(attribute_idx); - auto const attr_meta = MetaData::get_attribute_by_idx(comp_meta, attribute_idx); + auto const* attr_meta = MetaData::get_attribute_by_idx(comp_meta, attribute_idx); auto const attribute_name = MetaData::attribute_name(attr_meta); CAPTURE(attribute_name); @@ -523,7 +523,7 @@ TEST_CASE("API model - all updates") { } TEST_CASE("API model - updates w/ alternating compute mode") { - State state; + State const state; auto const input_dataset = state.get_input_dataset(); auto model = Model{50.0, input_dataset}; @@ -679,13 +679,12 @@ TEST_CASE("API model - incomplete input") { auto const output_type = symmetry == PGM_symmetric ? "sym_output"s : "asym_output"s; SUBCASE(calculation_symmetry.c_str()) { - auto n_bytes = complete_state.node_id.size() * - MetaData::component_size(MetaData::get_component_by_name(output_type, "node")); + auto const* node_output_meta = MetaData::get_component_by_name(output_type, "node"); - std::vector test_node_output(n_bytes, 0); + Buffer test_node_output(node_output_meta, std::ssize(complete_state.node_id)); DatasetMutable test_result_data{output_type, true, 1}; - test_result_data.add_buffer("node", std::ssize(test_node_output), std::ssize(test_node_output), nullptr, - test_node_output.data()); + test_result_data.add_buffer("node", test_node_output.size(), test_node_output.size(), nullptr, + test_node_output); SUBCASE("Target dataset") { CHECK_THROWS_WITH_AS(test_model.calculate(get_default_options(symmetry, PGM_linear), test_result_data), @@ -762,10 +761,10 @@ TEST_CASE("API model - incomplete input") { complete_state.asym_load_p_specified.data()); auto ref_model = Model{50.0, complete_state.get_input_dataset()}; - std::vector ref_node_output(n_bytes, 0); + Buffer ref_node_output(node_output_meta, std::ssize(complete_state.node_id)); DatasetMutable ref_result_data{output_type, true, 1}; - ref_result_data.add_buffer("node", std::ssize(ref_node_output), std::ssize(ref_node_output), nullptr, - ref_node_output.data()); + ref_result_data.add_buffer("node", ref_node_output.size(), ref_node_output.size(), nullptr, + ref_node_output); CHECK_NOTHROW(ref_model.calculate(get_default_options(symmetry, PGM_linear), ref_result_data)); @@ -779,27 +778,29 @@ TEST_CASE("API model - incomplete input") { complete_update_data)); } - CHECK(test_node_output == ref_node_output); - std::vector test_node_output_values(n_bytes, 0); - std::vector ref_node_output_values(n_bytes, 0); - - std::ranges::transform(test_node_output, test_node_output_values.begin(), - [](char c) { return static_cast(c); }); - std::ranges::transform(ref_node_output, ref_node_output_values.begin(), - [](char c) { return static_cast(c); }); - - CHECK(test_node_output_values == ref_node_output_values); - std::cout << "test: "; - for (auto i : test_node_output) { - std::cout << static_cast(i) << " "; - } - std::cout << "\n"; - - std::cout << "test: "; - for (auto i : ref_node_output) { - std::cout << static_cast(i) << " "; + for (Idx node_idx = 0; node_idx < complete_state.node_id.size(); ++node_idx) { + CAPTURE(node_idx); + auto const node_offset = node_idx * MetaData::component_size(node_output_meta); + + for (Idx attr_idx = 0; attr_idx < MetaData::n_attributes(node_output_meta); ++attr_idx) { + auto const* attr_meta = MetaData::get_attribute_by_idx(node_output_meta, attr_idx); + auto const attr_name = MetaData::attribute_name(attr_meta); + CAPTURE(attr_name); + + pgm_type_func_selector(attr_meta, [&] { + T test_value{nan_value()}; + T ref_value{nan_value()}; + test_node_output.get_value(attr_meta, &test_value, node_idx, 0); + ref_node_output.get_value(attr_meta, &ref_value, node_idx, 0); + + if constexpr (std::is_floating_point_v) { + CHECK(test_value == doctest::Approx(ref_value)); + } else { + CHECK(test_value == ref_value); + } + }); + } } - std::cout << "\n\n"; } } } From 81936f671804ac1b7b0ac77812e08566e16b21e1 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 15:45:49 +0100 Subject: [PATCH 36/49] resolve comments + fix ci Signed-off-by: Martijn Govers --- .../power_grid_model/include/power_grid_model/common/enum.hpp | 2 +- tests/native_api_tests/test_api_model_update.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp index 1cf10ab2d..a75961c74 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp @@ -24,7 +24,7 @@ enum class ControlSide : IntS { from = 0, to = 1, side_1 = 0, side_2 = 1, side_3 enum class CalculationType : IntS { power_flow = 0, state_estimation = 1, short_circuit = 2 }; -enum class CalculationSymmetry : IntS { symmetric = 1, asymmetric = 2 }; +enum class CalculationSymmetry : IntS { asymmetric = 0, symmetric = 1 }; enum class CalculationMethod : IntS { default_method = -128, diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index abb587fcc..8d715ef4a 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -778,9 +778,8 @@ TEST_CASE("API model - incomplete input") { complete_update_data)); } - for (Idx node_idx = 0; node_idx < complete_state.node_id.size(); ++node_idx) { + for (Idx node_idx = 0; node_idx < std::ssize(complete_state.node_id); ++node_idx) { CAPTURE(node_idx); - auto const node_offset = node_idx * MetaData::component_size(node_output_meta); for (Idx attr_idx = 0; attr_idx < MetaData::n_attributes(node_output_meta); ++attr_idx) { auto const* attr_meta = MetaData::get_attribute_by_idx(node_output_meta, attr_idx); From 20f82765a35ac93024b3fb48217fe1f332737a9e Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 16:12:08 +0100 Subject: [PATCH 37/49] Update power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp Signed-off-by: Martijn Govers Signed-off-by: Martijn Govers --- .../power_grid_model/auxiliary/serialization/deserializer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp index ba6cd8363..d1069bc6c 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp @@ -193,7 +193,7 @@ struct MapArrayVisitor : DefaultErrorVisitor> { static constexpr bool enable_array = std::same_as || std::same_as; static constexpr std::string_view static_err_msg = - enable_map ? (enable_array ? "Expected a map or array." : "Expected a map.") : "Expected an array."; + enable_map ? (enable_array ? "Expected a map or an array." : "Expected a map.") : "Expected an array."; Idx size{}; bool is_map{}; From 943ae981eed17ee38552b2bedf06e456022ae52d Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 3 Dec 2024 16:23:52 +0100 Subject: [PATCH 38/49] fix nightly build clang-tidy Signed-off-by: Martijn Govers --- tests/cpp_unit_tests/test_math_solver_pf.hpp | 2 +- tests/cpp_unit_tests/test_math_solver_se.hpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/cpp_unit_tests/test_math_solver_pf.hpp b/tests/cpp_unit_tests/test_math_solver_pf.hpp index 706f53940..85995ef66 100644 --- a/tests/cpp_unit_tests/test_math_solver_pf.hpp +++ b/tests/cpp_unit_tests/test_math_solver_pf.hpp @@ -71,7 +71,7 @@ template struct PFSolverTestGrid : public SteadyStateSol }; TEST_CASE_TEMPLATE_DEFINE("Test math solver - PF", SolverType, test_math_solver_pf_id) { - using sym = SolverType::sym; + using sym = typename SolverType::sym; PFSolverTestGrid grid; diff --git a/tests/cpp_unit_tests/test_math_solver_se.hpp b/tests/cpp_unit_tests/test_math_solver_se.hpp index edebd084c..93c4f65fe 100644 --- a/tests/cpp_unit_tests/test_math_solver_se.hpp +++ b/tests/cpp_unit_tests/test_math_solver_se.hpp @@ -128,14 +128,14 @@ TEST_CASE_TEMPLATE_DEFINE("Test math solver - SE", SolverType, test_math_solver_ constexpr auto error_tolerance{1e-10}; constexpr auto num_iter{20}; - using sym = SolverType::sym; + using sym = typename SolverType::sym; - SESolverTestGrid grid; + SESolverTestGrid const grid; // topo and param ptr auto param_ptr = std::make_shared const>(grid.param()); auto topo_ptr = std::make_shared(grid.topo()); - YBus y_bus{topo_ptr, param_ptr}; + YBus const y_bus{topo_ptr, param_ptr}; SUBCASE("Test se with angle") { SolverType solver{y_bus, topo_ptr}; From e6a4ea8deaffc46210e457dd9bbf44d833b84b08 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Wed, 4 Dec 2024 09:10:04 +0100 Subject: [PATCH 39/49] fix full clang-tidy build Signed-off-by: Martijn Govers --- tests/cpp_unit_tests/test_math_solver_pf.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cpp_unit_tests/test_math_solver_pf.hpp b/tests/cpp_unit_tests/test_math_solver_pf.hpp index 85995ef66..1939cff45 100644 --- a/tests/cpp_unit_tests/test_math_solver_pf.hpp +++ b/tests/cpp_unit_tests/test_math_solver_pf.hpp @@ -73,7 +73,7 @@ template struct PFSolverTestGrid : public SteadyStateSol TEST_CASE_TEMPLATE_DEFINE("Test math solver - PF", SolverType, test_math_solver_pf_id) { using sym = typename SolverType::sym; - PFSolverTestGrid grid; + PFSolverTestGrid const grid; // topo and param ptr auto param_ptr = std::make_shared const>(grid.param()); @@ -90,7 +90,7 @@ TEST_CASE_TEMPLATE_DEFINE("Test math solver - PF", SolverType, test_math_solver_ CalculationInfo info; PowerFlowInput const pf_input = grid.pf_input(); - SolverOutput output = run_power_flow(solver, y_bus, pf_input, error_tolerance, num_iter, info); + SolverOutput const output = run_power_flow(solver, y_bus, pf_input, error_tolerance, num_iter, info); assert_output(output, grid.output_ref(), false, result_tolerance); } From 08a0b6bdd564a3c2abe6e0f1dcc3dbdc49b56391 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Wed, 4 Dec 2024 09:17:16 +0100 Subject: [PATCH 40/49] use bool is_batch instead of Idx in cpp wrapper Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 32 ++++--------------- .../cpp_validation_tests/test_validation.cpp | 4 +-- .../test_api_serialization.cpp | 2 +- 3 files changed, 10 insertions(+), 28 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp index 596491631..4ac301688 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp @@ -25,7 +25,7 @@ class DatasetInfo { std::string name() const { return std::string{handle_.call_with(PGM_dataset_info_name, info_)}; } - Idx is_batch() const { return handle_.call_with(PGM_dataset_info_is_batch, info_); } + bool is_batch() const { return handle_.call_with(PGM_dataset_info_is_batch, info_) != 0; } Idx batch_size() const { return handle_.call_with(PGM_dataset_info_batch_size, info_); } @@ -88,19 +88,10 @@ class DatasetWritable { class DatasetMutable { public: - template || std::is_same_v>> - DatasetMutable(std::string const& dataset, T is_batch, Idx batch_size) + explicit DatasetMutable(std::string const& dataset, bool is_batch, Idx batch_size) : handle_{}, - dataset_{handle_.call_with( - PGM_create_dataset_mutable, dataset.c_str(), - [is_batch]() -> Idx { - if constexpr (std::is_same_v) { - return is_batch ? Idx{1} : Idx{0}; - } else { - return is_batch; - } - }(), - batch_size)}, + dataset_{ + handle_.call_with(PGM_create_dataset_mutable, dataset.c_str(), (is_batch ? Idx{1} : Idx{0}), batch_size)}, info_{handle_.call_with(PGM_dataset_mutable_get_info, get())} {} RawMutableDataset const* get() const { return dataset_.get(); } @@ -137,19 +128,10 @@ class DatasetMutable { class DatasetConst { public: - template || std::is_same_v>> - DatasetConst(std::string const& dataset, T is_batch, Idx batch_size) + explicit DatasetConst(std::string const& dataset, bool is_batch, Idx batch_size) : handle_{}, - dataset_{handle_.call_with( - PGM_create_dataset_const, dataset.c_str(), - [is_batch]() -> Idx { - if constexpr (std::is_same_v) { - return is_batch ? Idx{1} : Idx{0}; - } else { - return is_batch; - } - }(), - batch_size)}, + dataset_{ + handle_.call_with(PGM_create_dataset_const, dataset.c_str(), (is_batch ? Idx{1} : Idx{0}), batch_size)}, info_{handle_.call_with(PGM_dataset_const_get_info, get())} {} DatasetConst(DatasetWritable const& writable_dataset) diff --git a/tests/cpp_validation_tests/test_validation.cpp b/tests/cpp_validation_tests/test_validation.cpp index 24cc40c5a..a7cfb70c5 100644 --- a/tests/cpp_validation_tests/test_validation.cpp +++ b/tests/cpp_validation_tests/test_validation.cpp @@ -73,7 +73,7 @@ struct OwningDataset { OwningDataset create_owning_dataset(DatasetWritable& writable_dataset) { auto const& info = writable_dataset.get_info(); - Idx const is_batch = info.is_batch(); + bool const is_batch = info.is_batch(); Idx const batch_size = info.batch_size(); auto const& dataset_name = info.name(); OwningDataset owning_dataset{.dataset{DatasetMutable{dataset_name, is_batch, batch_size}}, @@ -101,7 +101,7 @@ OwningDataset create_owning_dataset(DatasetWritable& writable_dataset) { return owning_dataset; } -OwningDataset create_result_dataset(OwningDataset const& input, std::string const& dataset_name, Idx is_batch = 0, +OwningDataset create_result_dataset(OwningDataset const& input, std::string const& dataset_name, bool is_batch = false, Idx batch_size = 1) { OwningDataset owning_dataset{.dataset{DatasetMutable{dataset_name, is_batch, batch_size}}, .const_dataset = std::nullopt}; diff --git a/tests/native_api_tests/test_api_serialization.cpp b/tests/native_api_tests/test_api_serialization.cpp index caa3d67c8..9b11bff56 100644 --- a/tests/native_api_tests/test_api_serialization.cpp +++ b/tests/native_api_tests/test_api_serialization.cpp @@ -47,7 +47,7 @@ TEST_CASE("API Serialization and Deserialization") { Idx const n_components = 2; Idx const batch_size = 1; - Idx const is_batch = 0; + bool const is_batch = false; std::vector const elements_per_scenario = {1, 2}; std::vector const elements_per_scenario_complete = {1, 1}; std::vector const total_elements = {1, 2}; From 5cb7a28f29c4e3d9855f091da2da2e51bb9b18ec Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Wed, 4 Dec 2024 09:41:32 +0100 Subject: [PATCH 41/49] more full clang-tidy Signed-off-by: Martijn Govers --- tests/cpp_unit_tests/test_math_solver_pf_newton_raphson.cpp | 2 +- tests/cpp_validation_tests/test_validation.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cpp_unit_tests/test_math_solver_pf_newton_raphson.cpp b/tests/cpp_unit_tests/test_math_solver_pf_newton_raphson.cpp index 3f4572491..96d84ad4e 100644 --- a/tests/cpp_unit_tests/test_math_solver_pf_newton_raphson.cpp +++ b/tests/cpp_unit_tests/test_math_solver_pf_newton_raphson.cpp @@ -16,7 +16,7 @@ TYPE_TO_STRING_AS("NewtonRaphsonPFSolver", namespace power_grid_model::math_solver { namespace { using newton_raphson_pf::PFJacBlock; -} +} // namespace TEST_CASE("Test block") { SUBCASE("symmetric") { diff --git a/tests/cpp_validation_tests/test_validation.cpp b/tests/cpp_validation_tests/test_validation.cpp index a7cfb70c5..1102dbf26 100644 --- a/tests/cpp_validation_tests/test_validation.cpp +++ b/tests/cpp_validation_tests/test_validation.cpp @@ -589,7 +589,7 @@ void validate_batch_case(CaseParam const& param) { auto const& info = validation_case.update_batch.value().const_dataset.value().get_info(); Idx const batch_size = info.batch_size(); auto const batch_result = - create_result_dataset(validation_case.output_batch.value(), output_prefix, Idx{1}, batch_size); + create_result_dataset(validation_case.output_batch.value(), output_prefix, true, batch_size); // create model Model model{50.0, validation_case.input.const_dataset.value()}; From 8f4eef4326b360db4b371f24e7d346e4227f6bf2 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Wed, 4 Dec 2024 10:32:16 +0100 Subject: [PATCH 42/49] sonar cloud Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 10 +- .../cpp_validation_tests/test_validation.cpp | 2 +- tests/native_api_tests/test_api_model.cpp | 124 +++++++++--------- .../test_api_model_update.cpp | 32 ++--- 4 files changed, 84 insertions(+), 84 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp index 4ac301688..45991884c 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp @@ -89,9 +89,8 @@ class DatasetWritable { class DatasetMutable { public: explicit DatasetMutable(std::string const& dataset, bool is_batch, Idx batch_size) - : handle_{}, - dataset_{ - handle_.call_with(PGM_create_dataset_mutable, dataset.c_str(), (is_batch ? Idx{1} : Idx{0}), batch_size)}, + : dataset_{handle_.call_with(PGM_create_dataset_mutable, dataset.c_str(), (is_batch ? Idx{1} : Idx{0}), + batch_size)}, info_{handle_.call_with(PGM_dataset_mutable_get_info, get())} {} RawMutableDataset const* get() const { return dataset_.get(); } @@ -129,9 +128,8 @@ class DatasetMutable { class DatasetConst { public: explicit DatasetConst(std::string const& dataset, bool is_batch, Idx batch_size) - : handle_{}, - dataset_{ - handle_.call_with(PGM_create_dataset_const, dataset.c_str(), (is_batch ? Idx{1} : Idx{0}), batch_size)}, + : dataset_{handle_.call_with(PGM_create_dataset_const, dataset.c_str(), (is_batch ? Idx{1} : Idx{0}), + batch_size)}, info_{handle_.call_with(PGM_dataset_const_get_info, get())} {} DatasetConst(DatasetWritable const& writable_dataset) diff --git a/tests/cpp_validation_tests/test_validation.cpp b/tests/cpp_validation_tests/test_validation.cpp index 1102dbf26..c3316acd6 100644 --- a/tests/cpp_validation_tests/test_validation.cpp +++ b/tests/cpp_validation_tests/test_validation.cpp @@ -132,7 +132,7 @@ OwningDataset load_dataset(std::filesystem::path const& path) { // Issue in msgpack, reported in https://github.com/msgpack/msgpack-c/issues/1098 // May be a Clang Analyzer bug #ifndef __clang_analyzer__ // TODO(mgovers): re-enable this when issue in msgpack is fixed - Deserializer deserializer{read_file(path), Idx{0}}; + Deserializer deserializer{read_file(path), PGM_json}; auto& writable_dataset = deserializer.get_dataset(); auto dataset = create_owning_dataset(writable_dataset); deserializer.parse_to_buffer(); diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index e9fb186aa..9808715dd 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -279,35 +279,35 @@ TEST_CASE("API Model") { } SUBCASE("Test get indexer") { - std::vector const node_id{1, 2, 3}; - std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; + std::vector const node_id_2{1, 2, 3}; + std::vector const node_u_rated_2{10.0e3, 10.0e3, 10.0e3}; - DatasetConst input_dataset{"input", false, 1}; - input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); - input_dataset.add_attribute_buffer("node", "id", node_id.data()); - input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); + DatasetConst input_dataset_2{"input", false, 1}; + input_dataset_2.add_buffer("node", std::ssize(node_id_2), std::ssize(node_id_2), nullptr, nullptr); + input_dataset_2.add_attribute_buffer("node", "id", node_id_2.data()); + input_dataset_2.add_attribute_buffer("node", "u_rated", node_u_rated_2.data()); - auto model2 = Model{50.0, input_dataset}; + auto model_2 = Model{50.0, input_dataset_2}; SUBCASE("Good weather") { std::vector const ids_to_index{2, 1, 3, 2}; std::vector const expected_indexer{1, 0, 2, 1}; std::vector indexer(ids_to_index.size()); - model2.get_indexer("node", std::ssize(ids_to_index), ids_to_index.data(), indexer.data()); + model_2.get_indexer("node", std::ssize(ids_to_index), ids_to_index.data(), indexer.data()); CHECK(indexer == expected_indexer); } SUBCASE("Bad weather: wrong id") { std::vector const ids_to_index{2, 1, 3, 4}; std::vector indexer(ids_to_index.size()); CHECK_THROWS_WITH_AS( - model2.get_indexer("node", std::ssize(ids_to_index), ids_to_index.data(), indexer.data()), + model_2.get_indexer("node", std::ssize(ids_to_index), ids_to_index.data(), indexer.data()), doctest::Contains("The id cannot be found: 4"), PowerGridRegularError); } SUBCASE("Bad weather: wrong type") { std::vector const ids_to_index{2, 1, 3, 2}; std::vector indexer(ids_to_index.size()); CHECK_THROWS_WITH_AS( - model2.get_indexer("sym_load", std::ssize(ids_to_index), ids_to_index.data(), indexer.data()), + model_2.get_indexer("sym_load", std::ssize(ids_to_index), ids_to_index.data(), indexer.data()), doctest::Contains("Wrong type for object with id 2"), PowerGridRegularError); } } @@ -868,46 +868,46 @@ TEST_CASE("API Model") { } SUBCASE("Test duplicated id") { - std::vector node_id{1, 1, 3}; - DatasetConst input_dataset{"input", false, 1}; + std::vector node_id_2{1, 1, 3}; + DatasetConst input_dataset_2{"input", false, 1}; - input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); - input_dataset.add_attribute_buffer("node", "id", node_id.data()); + input_dataset_2.add_buffer("node", std::ssize(node_id_2), std::ssize(node_id_2), nullptr, nullptr); + input_dataset_2.add_attribute_buffer("node", "id", node_id_2.data()); - auto construct_model = [&] { Model{50.0, input_dataset}; }; + auto construct_model = [&] { Model{50.0, input_dataset_2}; }; CHECK_THROWS_WITH_AS(construct_model(), "Conflicting id detected: 1\n", PowerGridRegularError); } SUBCASE("Test non-existing id") { - std::vector const node_id{1, 2, 3}; - std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; + std::vector const node_id_2{1, 2, 3}; + std::vector const node_u_rated_2{10.0e3, 10.0e3, 10.0e3}; std::vector link_id{5}; std::vector link_from_node{99}; std::vector link_to_node{3}; - DatasetConst input_dataset{"input", false, 1}; + DatasetConst input_dataset_2{"input", false, 1}; - input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); - input_dataset.add_attribute_buffer("node", "id", node_id.data()); - input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); + input_dataset_2.add_buffer("node", std::ssize(node_id_2), std::ssize(node_id_2), nullptr, nullptr); + input_dataset_2.add_attribute_buffer("node", "id", node_id_2.data()); + input_dataset_2.add_attribute_buffer("node", "u_rated", node_u_rated_2.data()); - input_dataset.add_buffer("link", std::ssize(link_id), std::ssize(link_id), nullptr, nullptr); - input_dataset.add_attribute_buffer("link", "id", link_id.data()); - input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); - input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); + input_dataset_2.add_buffer("link", std::ssize(link_id), std::ssize(link_id), nullptr, nullptr); + input_dataset_2.add_attribute_buffer("link", "id", link_id.data()); + input_dataset_2.add_attribute_buffer("link", "from_node", link_from_node.data()); + input_dataset_2.add_attribute_buffer("link", "to_node", link_to_node.data()); - auto construct_model = [&] { Model{50.0, input_dataset}; }; + auto construct_model = [&] { Model{50.0, input_dataset_2}; }; CHECK_THROWS_WITH_AS(construct_model(), "The id cannot be found: 99\n", PowerGridRegularError); } SUBCASE("Test id for wrong type") { - std::vector const node_id{1, 2, 3}; - std::vector const node_u_rated{10.0e3, 10.0e3, 10.0e3}; + std::vector const node_id_2{1, 2, 3}; + std::vector const node_u_rated_2{10.0e3, 10.0e3, 10.0e3}; - std::vector line_id{9}; - std::vector line_from_node{1}; - std::vector line_to_node{2}; + std::vector line_id_2{9}; + std::vector line_from_node_2{1}; + std::vector line_to_node_2{2}; std::vector link_id{5}; std::vector link_from_node{2}; @@ -920,37 +920,37 @@ TEST_CASE("API Model") { std::vector sym_power_sensor_measured_object{3}; std::vector sym_power_sensor_measured_terminal_type{MeasuredTerminalType::node}; - DatasetConst input_dataset{"input", false, 1}; - - input_dataset.add_buffer("node", std::ssize(node_id), std::ssize(node_id), nullptr, nullptr); - input_dataset.add_attribute_buffer("node", "id", node_id.data()); - input_dataset.add_attribute_buffer("node", "u_rated", node_u_rated.data()); - - input_dataset.add_buffer("line", std::ssize(line_id), std::ssize(line_id), nullptr, nullptr); - input_dataset.add_attribute_buffer("line", "id", line_id.data()); - input_dataset.add_attribute_buffer("line", "from_node", line_from_node.data()); - input_dataset.add_attribute_buffer("line", "to_node", line_to_node.data()); - - input_dataset.add_buffer("link", std::ssize(link_id), std::ssize(link_id), nullptr, nullptr); - input_dataset.add_attribute_buffer("link", "id", link_id.data()); - input_dataset.add_attribute_buffer("link", "from_node", link_from_node.data()); - input_dataset.add_attribute_buffer("link", "to_node", link_to_node.data()); - - input_dataset.add_buffer("sym_voltage_sensor", std::ssize(sym_voltage_sensor_id), - std::ssize(sym_voltage_sensor_id), nullptr, nullptr); - input_dataset.add_attribute_buffer("sym_voltage_sensor", "id", sym_voltage_sensor_id.data()); - input_dataset.add_attribute_buffer("sym_voltage_sensor", "measured_object", - sym_voltage_sensor_measured_object.data()); - - input_dataset.add_buffer("sym_power_sensor", std::ssize(sym_power_sensor_id), std::ssize(sym_power_sensor_id), - nullptr, nullptr); - input_dataset.add_attribute_buffer("sym_power_sensor", "id", sym_power_sensor_id.data()); - input_dataset.add_attribute_buffer("sym_power_sensor", "measured_object", - sym_power_sensor_measured_object.data()); - input_dataset.add_attribute_buffer("sym_power_sensor", "measured_terminal_type", - sym_power_sensor_measured_terminal_type.data()); - - auto construct_model = [&] { Model{50.0, input_dataset}; }; + DatasetConst input_dataset_2{"input", false, 1}; + + input_dataset_2.add_buffer("node", std::ssize(node_id_2), std::ssize(node_id_2), nullptr, nullptr); + input_dataset_2.add_attribute_buffer("node", "id", node_id_2.data()); + input_dataset_2.add_attribute_buffer("node", "u_rated", node_u_rated_2.data()); + + input_dataset_2.add_buffer("line", std::ssize(line_id_2), std::ssize(line_id_2), nullptr, nullptr); + input_dataset_2.add_attribute_buffer("line", "id", line_id_2.data()); + input_dataset_2.add_attribute_buffer("line", "from_node", line_from_node_2.data()); + input_dataset_2.add_attribute_buffer("line", "to_node", line_to_node_2.data()); + + input_dataset_2.add_buffer("link", std::ssize(link_id), std::ssize(link_id), nullptr, nullptr); + input_dataset_2.add_attribute_buffer("link", "id", link_id.data()); + input_dataset_2.add_attribute_buffer("link", "from_node", link_from_node.data()); + input_dataset_2.add_attribute_buffer("link", "to_node", link_to_node.data()); + + input_dataset_2.add_buffer("sym_voltage_sensor", std::ssize(sym_voltage_sensor_id), + std::ssize(sym_voltage_sensor_id), nullptr, nullptr); + input_dataset_2.add_attribute_buffer("sym_voltage_sensor", "id", sym_voltage_sensor_id.data()); + input_dataset_2.add_attribute_buffer("sym_voltage_sensor", "measured_object", + sym_voltage_sensor_measured_object.data()); + + input_dataset_2.add_buffer("sym_power_sensor", std::ssize(sym_power_sensor_id), std::ssize(sym_power_sensor_id), + nullptr, nullptr); + input_dataset_2.add_attribute_buffer("sym_power_sensor", "id", sym_power_sensor_id.data()); + input_dataset_2.add_attribute_buffer("sym_power_sensor", "measured_object", + sym_power_sensor_measured_object.data()); + input_dataset_2.add_attribute_buffer("sym_power_sensor", "measured_terminal_type", + sym_power_sensor_measured_terminal_type.data()); + + auto construct_model = [&] { Model{50.0, input_dataset_2}; }; SUBCASE("Correct type") { CHECK_NOTHROW(construct_model()); } SUBCASE("Wrong branch terminal node") { diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 8d715ef4a..0614b2535 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -493,7 +493,8 @@ TEST_CASE("API model - all updates") { auto const attribute_name = MetaData::attribute_name(attr_meta); CAPTURE(attribute_name); - pgm_type_func_selector(attr_meta, [&]() { + pgm_type_func_selector(attr_meta, [&model, &update_data, &output_dataset_type, &comp_type, &attribute_name, + elements_per_scenario, total_elements]() { std::vector sym_output_from_batch(total_elements); std::vector sym_output_from_updated_single(total_elements); @@ -527,7 +528,7 @@ TEST_CASE("API model - updates w/ alternating compute mode") { auto const input_dataset = state.get_input_dataset(); auto model = Model{50.0, input_dataset}; - auto const check_sym = [&] { + auto const check_sym = [&model] { std::vector sym_node_output_u_pu(3); std::vector sym_line_output_i_from(1); std::vector sym_source_output_i(2); @@ -566,7 +567,7 @@ TEST_CASE("API model - updates w/ alternating compute mode") { CHECK(sym_asym_load_output_i[0] == doctest::Approx(0.0)); CHECK(sym_shunt_output_i[0] == doctest::Approx(0.0)); }; - auto const check_asym = [&] { + auto const check_asym = [&model] { std::vector asym_node_output_u_pu(9); std::vector asym_line_output_i_from(3); std::vector asym_source_output_i(6); @@ -786,18 +787,19 @@ TEST_CASE("API model - incomplete input") { auto const attr_name = MetaData::attribute_name(attr_meta); CAPTURE(attr_name); - pgm_type_func_selector(attr_meta, [&] { - T test_value{nan_value()}; - T ref_value{nan_value()}; - test_node_output.get_value(attr_meta, &test_value, node_idx, 0); - ref_node_output.get_value(attr_meta, &ref_value, node_idx, 0); - - if constexpr (std::is_floating_point_v) { - CHECK(test_value == doctest::Approx(ref_value)); - } else { - CHECK(test_value == ref_value); - } - }); + pgm_type_func_selector(attr_meta, + [&test_node_output, &ref_node_output, attr_meta, node_idx] { + T test_value{nan_value()}; + T ref_value{nan_value()}; + test_node_output.get_value(attr_meta, &test_value, node_idx, 0); + ref_node_output.get_value(attr_meta, &ref_value, node_idx, 0); + + if constexpr (std::is_floating_point_v) { + CHECK(test_value == doctest::Approx(ref_value)); + } else { + CHECK(test_value == ref_value); + } + }); } } } From a7b018529f9953cdee6e75dbad4a6912fdf4509c Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Wed, 4 Dec 2024 13:09:23 +0100 Subject: [PATCH 43/49] apply suggestions from code review Signed-off-by: Martijn Govers --- .../auxiliary/serialization/deserializer.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp index ba6cd8363..0c9c860ba 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp @@ -193,7 +193,7 @@ struct MapArrayVisitor : DefaultErrorVisitor> { static constexpr bool enable_array = std::same_as || std::same_as; static constexpr std::string_view static_err_msg = - enable_map ? (enable_array ? "Expected a map or array." : "Expected a map.") : "Expected an array."; + enable_map ? (enable_array ? "Map or array expected." : "Map expected.") : "Array expected."; Idx size{}; bool is_map{}; @@ -226,7 +226,7 @@ struct MapArrayVisitor : DefaultErrorVisitor> { }; struct StringVisitor : DefaultErrorVisitor { - static constexpr std::string_view static_err_msg = "Expected a string."; + static constexpr std::string_view static_err_msg = "String expected."; std::string_view str{}; bool visit_str(const char* v, uint32_t size) { @@ -236,7 +236,7 @@ struct StringVisitor : DefaultErrorVisitor { }; struct BoolVisitor : DefaultErrorVisitor { - static constexpr std::string_view static_err_msg = "Expected a boolean."; + static constexpr std::string_view static_err_msg = "Boolean expected."; bool value{}; bool visit_boolean(bool v) { @@ -248,7 +248,7 @@ struct BoolVisitor : DefaultErrorVisitor { template struct ValueVisitor; template struct ValueVisitor : DefaultErrorVisitor> { - static constexpr std::string_view static_err_msg = "Expected an integer."; + static constexpr std::string_view static_err_msg = "Integer expected."; T& value; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) @@ -270,7 +270,7 @@ template struct ValueVisitor : DefaultErrorVisitor struct ValueVisitor : DefaultErrorVisitor> { - static constexpr std::string_view static_err_msg = "Expected a number."; + static constexpr std::string_view static_err_msg = "Number expected."; double& value; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) @@ -294,7 +294,7 @@ template <> struct ValueVisitor : DefaultErrorVisitor struct ValueVisitor> : DefaultErrorVisitor>> { - static constexpr std::string_view static_err_msg = "Expected an array of 3 numbers."; + static constexpr std::string_view static_err_msg = "Array of 3 numbers expected."; RealValue& value; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) Idx idx{}; From 0387a6c0e6d9747aa2548090db81e0eaee0b8fad Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Thu, 5 Dec 2024 22:12:13 +0100 Subject: [PATCH 44/49] add bug test case Signed-off-by: Tony Xiang --- .../leaf-without-power-sensor/input.json | 27 +++++++++++++++++++ .../input.json.license | 3 +++ .../leaf-without-power-sensor/params.json | 8 ++++++ .../params.json.license | 3 +++ .../leaf-without-power-sensor/sym_output.json | 27 +++++++++++++++++++ .../sym_output.json.license | 3 +++ 6 files changed, 71 insertions(+) create mode 100644 tests/data/state_estimation/leaf-without-power-sensor/input.json create mode 100644 tests/data/state_estimation/leaf-without-power-sensor/input.json.license create mode 100644 tests/data/state_estimation/leaf-without-power-sensor/params.json create mode 100644 tests/data/state_estimation/leaf-without-power-sensor/params.json.license create mode 100644 tests/data/state_estimation/leaf-without-power-sensor/sym_output.json create mode 100644 tests/data/state_estimation/leaf-without-power-sensor/sym_output.json.license diff --git a/tests/data/state_estimation/leaf-without-power-sensor/input.json b/tests/data/state_estimation/leaf-without-power-sensor/input.json new file mode 100644 index 000000000..0302bec36 --- /dev/null +++ b/tests/data/state_estimation/leaf-without-power-sensor/input.json @@ -0,0 +1,27 @@ +{ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 0, "u_rated": 10}, + {"id": 1, "u_rated": 10} + ], + "line": [ + {"id": 2, "from_node": 0, "to_node": 1, "from_status": 1, "to_status": 1, "r1": 1.0, "x1": 0.0, "c1": 0.0, "tan1": 0.0, "i_n": 500} + ], + "source": [ + {"id": 3, "node": 0, "status": 1, "u_ref": 1.0} + ], + "sym_load": [ + {"id": 4, "node": 1, "status": 1, "type": 0, "p_specified": 9, "q_specified": 0} + ], + "sym_power_sensor": [ + {"id": 5, "measured_object": 2, "measured_terminal_type": 0, "power_sigma": 1, "p_measured": 10, "q_measured": 0} + ], + "sym_voltage_sensor": [ + {"id": 6, "measured_object": 0, "u_sigma": 1, "u_measured": 10} + ] + } +} \ No newline at end of file diff --git a/tests/data/state_estimation/leaf-without-power-sensor/input.json.license b/tests/data/state_estimation/leaf-without-power-sensor/input.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/state_estimation/leaf-without-power-sensor/input.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 diff --git a/tests/data/state_estimation/leaf-without-power-sensor/params.json b/tests/data/state_estimation/leaf-without-power-sensor/params.json new file mode 100644 index 000000000..10f5b5b7f --- /dev/null +++ b/tests/data/state_estimation/leaf-without-power-sensor/params.json @@ -0,0 +1,8 @@ +{ + "calculation_method": ["newton_raphson"], + "rtol": 1e-8, + "atol": { + "default": 1e-8, + ".+_residual": 5e-4 + } +} \ No newline at end of file diff --git a/tests/data/state_estimation/leaf-without-power-sensor/params.json.license b/tests/data/state_estimation/leaf-without-power-sensor/params.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/state_estimation/leaf-without-power-sensor/params.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 diff --git a/tests/data/state_estimation/leaf-without-power-sensor/sym_output.json b/tests/data/state_estimation/leaf-without-power-sensor/sym_output.json new file mode 100644 index 000000000..c0abb2f1c --- /dev/null +++ b/tests/data/state_estimation/leaf-without-power-sensor/sym_output.json @@ -0,0 +1,27 @@ +{ + "version": "1.0", + "type": "sym_output", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 0, "energized": 1, "u_pu": 1, "u": 10, "u_angle": 0}, + {"id": 1, "energized": 1, "u_pu": 0.9, "u": 9, "u_angle": 0} + ], + "line": [ + {"id": 2, "energized": 1, "p_from": 10, "q_from": 0, "p_to": -9, "q_to": -0} + ], + "source": [ + {"id": 3, "energized": 1, "p": 10, "q": 0} + ], + "sym_load": [ + {"id": 4, "energized": 1, "p": 9, "q": 0} + ], + "sym_power_sensor": [ + {"id": 5, "energized": 1, "p_residual": 0, "q_residual": 0} + ], + "sym_voltage_sensor": [ + {"id": 6, "energized": 1, "u_residual": 0} + ] + } +} \ No newline at end of file diff --git a/tests/data/state_estimation/leaf-without-power-sensor/sym_output.json.license b/tests/data/state_estimation/leaf-without-power-sensor/sym_output.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/state_estimation/leaf-without-power-sensor/sym_output.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 From a68bb7f2a149f5ea807f01c8e8f865a18200b250 Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Thu, 5 Dec 2024 22:15:22 +0100 Subject: [PATCH 45/49] reproduce error Signed-off-by: Tony Xiang --- .../data/state_estimation/leaf-without-power-sensor/input.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/state_estimation/leaf-without-power-sensor/input.json b/tests/data/state_estimation/leaf-without-power-sensor/input.json index 0302bec36..a6a823bcb 100644 --- a/tests/data/state_estimation/leaf-without-power-sensor/input.json +++ b/tests/data/state_estimation/leaf-without-power-sensor/input.json @@ -18,7 +18,7 @@ {"id": 4, "node": 1, "status": 1, "type": 0, "p_specified": 9, "q_specified": 0} ], "sym_power_sensor": [ - {"id": 5, "measured_object": 2, "measured_terminal_type": 0, "power_sigma": 1, "p_measured": 10, "q_measured": 0} + {"id": 5, "measured_object": 3, "measured_terminal_type": 2, "power_sigma": 1, "p_measured": 10, "q_measured": 0} ], "sym_voltage_sensor": [ {"id": 6, "measured_object": 0, "u_sigma": 1, "u_measured": 10} From 3d5edca737d6b653a7897af037ad4372dbc33002 Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Thu, 5 Dec 2024 22:39:26 +0100 Subject: [PATCH 46/49] reproduce middle sensor Signed-off-by: Tony Xiang --- .../leaf-without-power-sensor/params.json | 2 +- .../middle-without-power-sensor/input.json | 29 +++++++++++++++++++ .../input.json.license | 3 ++ .../middle-without-power-sensor/params.json | 8 +++++ .../params.json.license | 3 ++ .../sym_output.json | 29 +++++++++++++++++++ .../sym_output.json.license | 3 ++ 7 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 tests/data/state_estimation/middle-without-power-sensor/input.json create mode 100644 tests/data/state_estimation/middle-without-power-sensor/input.json.license create mode 100644 tests/data/state_estimation/middle-without-power-sensor/params.json create mode 100644 tests/data/state_estimation/middle-without-power-sensor/params.json.license create mode 100644 tests/data/state_estimation/middle-without-power-sensor/sym_output.json create mode 100644 tests/data/state_estimation/middle-without-power-sensor/sym_output.json.license diff --git a/tests/data/state_estimation/leaf-without-power-sensor/params.json b/tests/data/state_estimation/leaf-without-power-sensor/params.json index 10f5b5b7f..a0e22b030 100644 --- a/tests/data/state_estimation/leaf-without-power-sensor/params.json +++ b/tests/data/state_estimation/leaf-without-power-sensor/params.json @@ -1,5 +1,5 @@ { - "calculation_method": ["newton_raphson"], + "calculation_method": ["newton_raphson", "iterative_linear"], "rtol": 1e-8, "atol": { "default": 1e-8, diff --git a/tests/data/state_estimation/middle-without-power-sensor/input.json b/tests/data/state_estimation/middle-without-power-sensor/input.json new file mode 100644 index 000000000..60c774719 --- /dev/null +++ b/tests/data/state_estimation/middle-without-power-sensor/input.json @@ -0,0 +1,29 @@ +{ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 0, "u_rated": 10}, + {"id": 1, "u_rated": 10}, + {"id": 7, "u_rated": 10} + ], + "line": [ + {"id": 2, "from_node": 0, "to_node": 1, "from_status": 1, "to_status": 1, "r1": 1.0, "x1": 0.0, "c1": 0.0, "tan1": 0.0, "i_n": 500}, + {"id": 8, "from_node": 1, "to_node": 7, "from_status": 1, "to_status": 1, "r1": 1.0, "x1": 0.0, "c1": 0.0, "tan1": 0.0, "i_n": 500} + ], + "source": [ + {"id": 3, "node": 0, "status": 1, "u_ref": 1.0} + ], + "sym_load": [ + {"id": 4, "node": 1, "status": 1, "type": 0, "p_specified": 9, "q_specified": 0} + ], + "sym_power_sensor": [ + {"id": 5, "measured_object": 3, "measured_terminal_type": 2, "power_sigma": 1, "p_measured": 10, "q_measured": 0} + ], + "sym_voltage_sensor": [ + {"id": 6, "measured_object": 0, "u_sigma": 1, "u_measured": 10} + ] + } +} \ No newline at end of file diff --git a/tests/data/state_estimation/middle-without-power-sensor/input.json.license b/tests/data/state_estimation/middle-without-power-sensor/input.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/state_estimation/middle-without-power-sensor/input.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 diff --git a/tests/data/state_estimation/middle-without-power-sensor/params.json b/tests/data/state_estimation/middle-without-power-sensor/params.json new file mode 100644 index 000000000..a0e22b030 --- /dev/null +++ b/tests/data/state_estimation/middle-without-power-sensor/params.json @@ -0,0 +1,8 @@ +{ + "calculation_method": ["newton_raphson", "iterative_linear"], + "rtol": 1e-8, + "atol": { + "default": 1e-8, + ".+_residual": 5e-4 + } +} \ No newline at end of file diff --git a/tests/data/state_estimation/middle-without-power-sensor/params.json.license b/tests/data/state_estimation/middle-without-power-sensor/params.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/state_estimation/middle-without-power-sensor/params.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 diff --git a/tests/data/state_estimation/middle-without-power-sensor/sym_output.json b/tests/data/state_estimation/middle-without-power-sensor/sym_output.json new file mode 100644 index 000000000..9ea2249f5 --- /dev/null +++ b/tests/data/state_estimation/middle-without-power-sensor/sym_output.json @@ -0,0 +1,29 @@ +{ + "version": "1.0", + "type": "sym_output", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 0, "energized": 1, "u_pu": 1, "u": 10, "u_angle": 0}, + {"id": 1, "energized": 1, "u_pu": 0.9, "u": 9, "u_angle": 0}, + {"id": 7, "energized": 1, "u_pu": 0.9, "u": 9, "u_angle": 0} + ], + "line": [ + {"id": 2, "energized": 1, "p_from": 10, "q_from": 0, "p_to": -9, "q_to": 0}, + {"id": 8, "energized": 1, "p_from": 0, "q_from": 0, "p_to": 0, "q_to": 0} + ], + "source": [ + {"id": 3, "energized": 1, "p": 10, "q": 0} + ], + "sym_load": [ + {"id": 4, "energized": 1, "p": 9, "q": 0} + ], + "sym_power_sensor": [ + {"id": 5, "energized": 1, "p_residual": 0, "q_residual": 0} + ], + "sym_voltage_sensor": [ + {"id": 6, "energized": 1, "u_residual": 0} + ] + } +} \ No newline at end of file diff --git a/tests/data/state_estimation/middle-without-power-sensor/sym_output.json.license b/tests/data/state_estimation/middle-without-power-sensor/sym_output.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/state_estimation/middle-without-power-sensor/sym_output.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 From 6a3028bfab31afd211017687079cb9046f3c7787 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Fri, 6 Dec 2024 09:27:41 +0100 Subject: [PATCH 47/49] mark repro cases as xfail Signed-off-by: Martijn Govers --- .../state_estimation/leaf-without-power-sensor/params.json | 4 ++++ .../state_estimation/middle-without-power-sensor/params.json | 4 ++++ tests/unit/utils.py | 2 ++ 3 files changed, 10 insertions(+) diff --git a/tests/data/state_estimation/leaf-without-power-sensor/params.json b/tests/data/state_estimation/leaf-without-power-sensor/params.json index a0e22b030..4167e1fbf 100644 --- a/tests/data/state_estimation/leaf-without-power-sensor/params.json +++ b/tests/data/state_estimation/leaf-without-power-sensor/params.json @@ -4,5 +4,9 @@ "atol": { "default": 1e-8, ".+_residual": 5e-4 + }, + "fail": { + "raises": "SparseMatrixError", + "reason": "Bug in Sparse LU solver found in #853" } } \ No newline at end of file diff --git a/tests/data/state_estimation/middle-without-power-sensor/params.json b/tests/data/state_estimation/middle-without-power-sensor/params.json index a0e22b030..4167e1fbf 100644 --- a/tests/data/state_estimation/middle-without-power-sensor/params.json +++ b/tests/data/state_estimation/middle-without-power-sensor/params.json @@ -4,5 +4,9 @@ "atol": { "default": 1e-8, ".+_residual": 5e-4 + }, + "fail": { + "raises": "SparseMatrixError", + "reason": "Bug in Sparse LU solver found in #853" } } \ No newline at end of file diff --git a/tests/unit/utils.py b/tests/unit/utils.py index 6de4a7429..7d246e3fd 100644 --- a/tests/unit/utils.py +++ b/tests/unit/utils.py @@ -29,6 +29,7 @@ PowerGridBatchError, PowerGridError, PowerGridSerializationError, + SparseMatrixError, ) from power_grid_model.utils import json_deserialize, json_deserialize_from_file, json_serialize_to_file @@ -55,6 +56,7 @@ AutomaticTapCalculationError, InvalidTransformerClock, NotObservableError, + SparseMatrixError, PowerGridSerializationError, AssertionError, OSError, From e7927e880e84c00ee2fd561fe8b865f1031d16e4 Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Sun, 8 Dec 2024 18:59:52 +0100 Subject: [PATCH 48/49] more reproducible Signed-off-by: Tony Xiang --- .../state_estimation/middle-without-power-sensor/input.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/data/state_estimation/middle-without-power-sensor/input.json b/tests/data/state_estimation/middle-without-power-sensor/input.json index 60c774719..6790adc6c 100644 --- a/tests/data/state_estimation/middle-without-power-sensor/input.json +++ b/tests/data/state_estimation/middle-without-power-sensor/input.json @@ -20,7 +20,8 @@ {"id": 4, "node": 1, "status": 1, "type": 0, "p_specified": 9, "q_specified": 0} ], "sym_power_sensor": [ - {"id": 5, "measured_object": 3, "measured_terminal_type": 2, "power_sigma": 1, "p_measured": 10, "q_measured": 0} + {"id": 5, "measured_object": 3, "measured_terminal_type": 2, "power_sigma": 1, "p_measured": 10, "q_measured": 0}, + {"id": 9, "measured_object": 8, "measured_terminal_type": 0, "power_sigma": 1, "p_measured": 0, "q_measured": 0} ], "sym_voltage_sensor": [ {"id": 6, "measured_object": 0, "u_sigma": 1, "u_measured": 10} From 6c74e2621f32d9afce3c5ffd8f6e06980163775f Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 9 Dec 2024 08:15:17 +0100 Subject: [PATCH 49/49] fix compatibility mypy numpy==2.2.0 Signed-off-by: Martijn Govers --- tests/unit/validation/test_input_validation.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/unit/validation/test_input_validation.py b/tests/unit/validation/test_input_validation.py index eab6d2ceb..aef0061b5 100644 --- a/tests/unit/validation/test_input_validation.py +++ b/tests/unit/validation/test_input_validation.py @@ -238,12 +238,14 @@ def original_data() -> dict[ComponentType, np.ndarray]: asym_voltage_sensor = initialize_array(DatasetType.input, ComponentType.asym_voltage_sensor, 4) asym_voltage_sensor["id"] = [7, 8, 9, 10] asym_voltage_sensor["measured_object"] = [2, 3, 1, 200] - asym_voltage_sensor["u_measured"] = [ - [10.5e3, 10.4e3, 10.6e3], - [np.nan, np.nan, np.nan], - [0, 0, 0], - [-1e4, 1e4, 1e4], - ] + asym_voltage_sensor["u_measured"] = np.array( + [ + [10.5e3, 10.4e3, 10.6e3], + [np.nan, np.nan, np.nan], + [0, 0, 0], + [-1e4, 1e4, 1e4], + ] + ) asym_voltage_sensor["u_sigma"] = [1.0, np.nan, 0.0, -1.0] sym_power_sensor = initialize_array(DatasetType.input, ComponentType.sym_power_sensor, 4)