From 07b0cf1d1ce7517dcbdd65ce438ea99b8ed80366 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 5 Dec 2024 07:41:46 +0100 Subject: [PATCH 01/44] migrate owning dataset to C++ wrapper Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 16 +++ .../power_grid_model_cpp/serialization.hpp | 29 ++++++ .../cpp_validation_tests/test_validation.cpp | 97 +++++-------------- 3 files changed, 69 insertions(+), 73 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..b57ea091b 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 @@ -173,6 +173,22 @@ class DatasetConst { detail::UniquePtr dataset_; DatasetInfo info_; }; + +struct OwningMemory { + std::vector buffers; + std::vector> indptrs; +}; + +struct OwningDatasetMutable { + DatasetMutable dataset; + DatasetConst const_dataset; + OwningMemory storage{}; +}; + +struct OwningDatasetConst { + DatasetConst const_dataset; + OwningMemory storage{}; +}; } // namespace power_grid_model_cpp #endif // POWER_GRID_MODEL_CPP_DATASET_HPP diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp index bb08fec4b..749474a78 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp @@ -90,6 +90,35 @@ class Serializer { power_grid_model_cpp::Handle handle_{}; detail::UniquePtr serializer_; }; + +inline OwningDatasetConst create_owning_dataset(DatasetWritable& writable_dataset) { + auto const& info = writable_dataset.get_info(); + bool const is_batch = info.is_batch(); + Idx const batch_size = info.batch_size(); + auto const& dataset_name = info.name(); + DatasetMutable dataset_mutable{dataset_name, is_batch, batch_size}; + DatasetConst dataset_const{dataset_mutable}; + OwningDatasetConst owning_dataset{.const_dataset{writable_dataset}}; + + for (Idx component_idx{}; component_idx < info.n_components(); ++component_idx) { + auto const& component_name = info.component_name(component_idx); + auto const& component_meta = MetaData::get_component_by_name(dataset_name, component_name); + Idx const component_elements_per_scenario = info.component_elements_per_scenario(component_idx); + Idx const component_size = info.component_total_elements(component_idx); + + auto& current_indptr = owning_dataset.storage.indptrs.emplace_back( + info.component_elements_per_scenario(component_idx) < 0 ? batch_size + 1 : 0); + if (!current_indptr.empty()) { + current_indptr.at(0) = 0; + current_indptr.at(batch_size) = component_size; + } + Idx* const indptr = current_indptr.empty() ? nullptr : current_indptr.data(); + auto& current_buffer = owning_dataset.storage.buffers.emplace_back(component_meta, component_size); + writable_dataset.set_buffer(component_name, indptr, current_buffer); + } + owning_dataset.const_dataset = writable_dataset; + return owning_dataset; +} } // namespace power_grid_model_cpp #endif // POWER_GRID_MODEL_CPP_SERIALIZATION_HPP diff --git a/tests/cpp_validation_tests/test_validation.cpp b/tests/cpp_validation_tests/test_validation.cpp index 1102dbf26..86992d58c 100644 --- a/tests/cpp_validation_tests/test_validation.cpp +++ b/tests/cpp_validation_tests/test_validation.cpp @@ -4,7 +4,7 @@ #define PGM_ENABLE_EXPERIMENTAL -#include "power_grid_model_cpp.hpp" +#include #include #include @@ -60,56 +60,14 @@ auto read_json(std::filesystem::path const& path) { return j; } -struct OwningMemory { - std::vector buffers; - std::vector> indptrs; -}; - -struct OwningDataset { - std::optional dataset; - std::optional const_dataset; - OwningMemory storage{}; -}; +OwningDatasetMutable create_result_dataset(OwningDatasetConst const& input, std::string const& dataset_name, + bool is_batch = false, Idx batch_size = 1) { + DatasetMutable dataset_mutable{dataset_name, is_batch, batch_size}; + DatasetConst dataset_const{dataset_mutable}; + OwningDatasetMutable owning_dataset{.dataset{std::move(dataset_mutable)}, + .const_dataset = std::move(dataset_const)}; -OwningDataset create_owning_dataset(DatasetWritable& writable_dataset) { - auto const& info = writable_dataset.get_info(); - 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}}, - .const_dataset = std::nullopt}; - - for (Idx component_idx{}; component_idx < info.n_components(); ++component_idx) { - auto const& component_name = info.component_name(component_idx); - auto const& component_meta = MetaData::get_component_by_name(dataset_name, component_name); - Idx const component_elements_per_scenario = info.component_elements_per_scenario(component_idx); - Idx const component_size = info.component_total_elements(component_idx); - - auto& current_indptr = owning_dataset.storage.indptrs.emplace_back( - info.component_elements_per_scenario(component_idx) < 0 ? batch_size + 1 : 0); - if (!current_indptr.empty()) { - current_indptr.at(0) = 0; - current_indptr.at(batch_size) = component_size; - } - Idx* const indptr = current_indptr.empty() ? nullptr : current_indptr.data(); - auto& current_buffer = owning_dataset.storage.buffers.emplace_back(component_meta, component_size); - writable_dataset.set_buffer(component_name, indptr, current_buffer); - owning_dataset.dataset.value().add_buffer(component_name, component_elements_per_scenario, component_size, - indptr, current_buffer); - } - owning_dataset.const_dataset = writable_dataset; - return owning_dataset; -} - -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}; - - if (!input.const_dataset.has_value()) { - throw OptionalNotInitialized("DatasetConst"); - } - DatasetInfo const& input_info = input.const_dataset.value().get_info(); + DatasetInfo const& input_info = input.const_dataset.get_info(); for (Idx component_idx{}; component_idx != input_info.n_components(); ++component_idx) { auto const& component_name = input_info.component_name(component_idx); @@ -121,14 +79,14 @@ OwningDataset create_result_dataset(OwningDataset const& input, std::string cons input_info.component_elements_per_scenario(component_idx) < 0 ? batch_size + 1 : 0); Idx const* const indptr = current_indptr.empty() ? nullptr : current_indptr.data(); auto& current_buffer = owning_dataset.storage.buffers.emplace_back(component_meta, component_size); - owning_dataset.dataset.value().add_buffer(component_name, component_elements_per_scenario, component_size, - indptr, current_buffer); + owning_dataset.dataset.add_buffer(component_name, component_elements_per_scenario, component_size, indptr, + current_buffer); } - owning_dataset.const_dataset = owning_dataset.dataset.value(); + owning_dataset.const_dataset = owning_dataset.dataset; return owning_dataset; } -OwningDataset load_dataset(std::filesystem::path const& path) { +OwningDatasetConst 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 @@ -255,23 +213,17 @@ void check_individual_attribute(Buffer const& buffer, Buffer const& ref_buffer, } } -void assert_result(OwningDataset const& owning_result, OwningDataset const& owning_reference_result, +void assert_result(OwningDatasetMutable const& owning_result, OwningDatasetConst const& owning_reference_result, std::map> atol, double rtol) { using namespace std::string_literals; - if (!owning_result.const_dataset.has_value()) { - throw OptionalNotInitialized("DatasetConst"); - } - DatasetConst const& result = owning_result.const_dataset.value(); + DatasetConst const& result = owning_result.const_dataset; auto const& result_info = result.get_info(); auto const& result_name = result_info.name(); Idx const result_batch_size = result_info.batch_size(); auto const& storage = owning_result.storage; - if (!owning_reference_result.const_dataset.has_value()) { - throw OptionalNotInitialized("DatasetConst"); - } - DatasetConst const& reference_result = owning_reference_result.const_dataset.value(); + DatasetConst const& reference_result = owning_reference_result.const_dataset; auto const& reference_result_info = reference_result.get_info(); auto const& reference_result_name = reference_result_info.name(); auto const& reference_storage = owning_reference_result.storage; @@ -490,10 +442,10 @@ void add_cases(std::filesystem::path const& case_dir, std::string const& calcula // test case with parameter struct ValidationCase { CaseParam param; - OwningDataset input; - std::optional output; - std::optional update_batch; - std::optional output_batch; + OwningDatasetConst input; + std::optional output; + std::optional update_batch; + std::optional output_batch; }; ValidationCase create_validation_case(CaseParam const& param, std::string const& output_type) { @@ -574,8 +526,8 @@ void validate_single_case(CaseParam const& param) { // create and run model auto const& options = get_options(param); - Model model{50.0, validation_case.input.const_dataset.value()}; - model.calculate(options, result.dataset.value()); + Model model{50.0, validation_case.input.const_dataset}; + model.calculate(options, result.dataset); // check results assert_result(result, validation_case.output.value(), param.atol, param.rtol); @@ -586,21 +538,20 @@ void validate_batch_case(CaseParam const& param) { execute_test(param, [&]() { auto const output_prefix = get_output_type(param.calculation_type, param.sym); auto const validation_case = create_validation_case(param, output_prefix); - auto const& info = validation_case.update_batch.value().const_dataset.value().get_info(); + auto const& info = validation_case.update_batch.value().const_dataset.get_info(); Idx const batch_size = info.batch_size(); auto const batch_result = 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()}; + Model model{50.0, validation_case.input.const_dataset}; // check results after whole update is finished for (Idx const threading : {-1, 0, 1, 2}) { CAPTURE(threading); // set options and run auto const& options = get_options(param, threading); - model.calculate(options, batch_result.dataset.value(), - validation_case.update_batch.value().const_dataset.value()); + model.calculate(options, batch_result.dataset, validation_case.update_batch.value().const_dataset); // check results assert_result(batch_result, validation_case.output_batch.value(), param.atol, param.rtol); From ae78da56985f267a3cc71141f6608e90fca4f832 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 5 Dec 2024 07:51:20 +0100 Subject: [PATCH 02/44] reduce complexity Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 3 +- .../power_grid_model_cpp/serialization.hpp | 11 ++++--- .../cpp_validation_tests/test_validation.cpp | 30 ++++++++----------- 3 files changed, 19 insertions(+), 25 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 b57ea091b..052b5d456 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 @@ -181,12 +181,11 @@ struct OwningMemory { struct OwningDatasetMutable { DatasetMutable dataset; - DatasetConst const_dataset; OwningMemory storage{}; }; struct OwningDatasetConst { - DatasetConst const_dataset; + DatasetConst dataset; OwningMemory storage{}; }; } // namespace power_grid_model_cpp diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp index 749474a78..185c768ec 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp @@ -98,7 +98,7 @@ inline OwningDatasetConst create_owning_dataset(DatasetWritable& writable_datase auto const& dataset_name = info.name(); DatasetMutable dataset_mutable{dataset_name, is_batch, batch_size}; DatasetConst dataset_const{dataset_mutable}; - OwningDatasetConst owning_dataset{.const_dataset{writable_dataset}}; + OwningMemory storage{}; for (Idx component_idx{}; component_idx < info.n_components(); ++component_idx) { auto const& component_name = info.component_name(component_idx); @@ -106,18 +106,17 @@ inline OwningDatasetConst create_owning_dataset(DatasetWritable& writable_datase Idx const component_elements_per_scenario = info.component_elements_per_scenario(component_idx); Idx const component_size = info.component_total_elements(component_idx); - auto& current_indptr = owning_dataset.storage.indptrs.emplace_back( - info.component_elements_per_scenario(component_idx) < 0 ? batch_size + 1 : 0); + auto& current_indptr = + storage.indptrs.emplace_back(info.component_elements_per_scenario(component_idx) < 0 ? batch_size + 1 : 0); if (!current_indptr.empty()) { current_indptr.at(0) = 0; current_indptr.at(batch_size) = component_size; } Idx* const indptr = current_indptr.empty() ? nullptr : current_indptr.data(); - auto& current_buffer = owning_dataset.storage.buffers.emplace_back(component_meta, component_size); + auto& current_buffer = storage.buffers.emplace_back(component_meta, component_size); writable_dataset.set_buffer(component_name, indptr, current_buffer); } - owning_dataset.const_dataset = writable_dataset; - return owning_dataset; + return OwningDatasetConst{.dataset = writable_dataset, .storage = std::move(storage)}; } } // namespace power_grid_model_cpp diff --git a/tests/cpp_validation_tests/test_validation.cpp b/tests/cpp_validation_tests/test_validation.cpp index 86992d58c..8688e0bda 100644 --- a/tests/cpp_validation_tests/test_validation.cpp +++ b/tests/cpp_validation_tests/test_validation.cpp @@ -62,12 +62,9 @@ auto read_json(std::filesystem::path const& path) { OwningDatasetMutable create_result_dataset(OwningDatasetConst const& input, std::string const& dataset_name, bool is_batch = false, Idx batch_size = 1) { - DatasetMutable dataset_mutable{dataset_name, is_batch, batch_size}; - DatasetConst dataset_const{dataset_mutable}; - OwningDatasetMutable owning_dataset{.dataset{std::move(dataset_mutable)}, - .const_dataset = std::move(dataset_const)}; + DatasetInfo const& input_info = input.dataset.get_info(); - DatasetInfo const& input_info = input.const_dataset.get_info(); + OwningDatasetMutable result{.dataset{dataset_name, is_batch, batch_size}, .storage{}}; for (Idx component_idx{}; component_idx != input_info.n_components(); ++component_idx) { auto const& component_name = input_info.component_name(component_idx); @@ -75,15 +72,14 @@ OwningDatasetMutable create_result_dataset(OwningDatasetConst const& input, std: Idx const component_elements_per_scenario = input_info.component_elements_per_scenario(component_idx); Idx const component_size = input_info.component_total_elements(component_idx); - auto& current_indptr = owning_dataset.storage.indptrs.emplace_back( + auto& current_indptr = result.storage.indptrs.emplace_back( input_info.component_elements_per_scenario(component_idx) < 0 ? batch_size + 1 : 0); Idx const* const indptr = current_indptr.empty() ? nullptr : current_indptr.data(); - auto& current_buffer = owning_dataset.storage.buffers.emplace_back(component_meta, component_size); - owning_dataset.dataset.add_buffer(component_name, component_elements_per_scenario, component_size, indptr, - current_buffer); + auto& current_buffer = result.storage.buffers.emplace_back(component_meta, component_size); + result.dataset.add_buffer(component_name, component_elements_per_scenario, component_size, indptr, + current_buffer); } - owning_dataset.const_dataset = owning_dataset.dataset; - return owning_dataset; + return result; } OwningDatasetConst load_dataset(std::filesystem::path const& path) { @@ -217,13 +213,13 @@ void assert_result(OwningDatasetMutable const& owning_result, OwningDatasetConst std::map> atol, double rtol) { using namespace std::string_literals; - DatasetConst const& result = owning_result.const_dataset; + DatasetConst const result{owning_result.dataset}; auto const& result_info = result.get_info(); auto const& result_name = result_info.name(); Idx const result_batch_size = result_info.batch_size(); auto const& storage = owning_result.storage; - DatasetConst const& reference_result = owning_reference_result.const_dataset; + DatasetConst const& reference_result = owning_reference_result.dataset; auto const& reference_result_info = reference_result.get_info(); auto const& reference_result_name = reference_result_info.name(); auto const& reference_storage = owning_reference_result.storage; @@ -526,7 +522,7 @@ void validate_single_case(CaseParam const& param) { // create and run model auto const& options = get_options(param); - Model model{50.0, validation_case.input.const_dataset}; + Model model{50.0, validation_case.input.dataset}; model.calculate(options, result.dataset); // check results @@ -538,20 +534,20 @@ void validate_batch_case(CaseParam const& param) { execute_test(param, [&]() { auto const output_prefix = get_output_type(param.calculation_type, param.sym); auto const validation_case = create_validation_case(param, output_prefix); - auto const& info = validation_case.update_batch.value().const_dataset.get_info(); + auto const& info = validation_case.update_batch.value().dataset.get_info(); Idx const batch_size = info.batch_size(); auto const batch_result = create_result_dataset(validation_case.output_batch.value(), output_prefix, true, batch_size); // create model - Model model{50.0, validation_case.input.const_dataset}; + Model model{50.0, validation_case.input.dataset}; // check results after whole update is finished for (Idx const threading : {-1, 0, 1, 2}) { CAPTURE(threading); // set options and run auto const& options = get_options(param, threading); - model.calculate(options, batch_result.dataset, validation_case.update_batch.value().const_dataset); + model.calculate(options, batch_result.dataset, validation_case.update_batch.value().dataset); // check results assert_result(batch_result, validation_case.output_batch.value(), param.atol, param.rtol); From 635e9261a09970d37a48994b56f775a6e64e4e05 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 5 Dec 2024 08:31:17 +0100 Subject: [PATCH 03/44] migrate API model - all updates test to JSON input Signed-off-by: Martijn Govers --- .../test_api_model_update.cpp | 191 +++++++++++++----- 1 file changed, 136 insertions(+), 55 deletions(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 0614b2535..cb21f3dbe 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -255,6 +256,7 @@ TEST_CASE_TEMPLATE( } namespace { +using namespace std::string_literals; using std::numbers::sqrt3; enum class CalculationSymmetry : Idx { symmetric = PGM_symmetric, asymmetric = PGM_asymmetric }; @@ -284,6 +286,14 @@ Options get_default_options(PGM_SymmetryType calculation_symmetry, PGM_Calculati return opt; } +OwningDatasetConst load_dataset(std::string const& json_string) { + Deserializer deserializer{json_string, PGM_json}; + auto& writable_dataset = deserializer.get_dataset(); + auto owning_dataset = create_owning_dataset(writable_dataset); + deserializer.parse_to_buffer(); + return owning_dataset; +} + 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; @@ -293,6 +303,65 @@ constexpr double i_shunt = 0.015 / 0.025 * i; constexpr double i_load = 0.005 / 0.025 * i; } // namespace test +auto const state_json = R"json({ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 1, "u_rated": 10000}, + {"id": 2, "u_rated": 10000}, + {"id": 3, "u_rated": 10000} + ], + "line": [ + {"id": 4, "from_node": 1, "to_node": 2, "from_status": 1, "to_status": 1, "r1": 10, "x1": 0, "c1": 0, "tan1": 0, "r0": 10, "x0": 0, "c0": 0, "tan0": 0, "i_n": 1000} + ], + "link": [ + {"id": 5, "from_node": 2, "to_node": 3, "from_status": 1, "to_status": 1} + ], + "source": [ + {"id": 6, "node": 1, "status": 1, "u_ref": 1.05, "sk": 1000000000000}, + {"id": 10, "node": 3, "status": 0, "u_ref": 1.05, "u_ref_angle": 0, "sk": 1000000000000} + ], + "sym_load": [ + {"id": 7, "node": 3, "status": 1, "type": 1, "p_specified": 500000, "q_specified": 0} + ], + "asym_load": [ + {"id": 8, "node": 3, "status": 1, "type": 1, "p_specified": [166666.6666666667, 166666.6666666667, 166666.6666666667], "q_specified": [0, 0, 0]} + ], + "shunt": [ + {"id": 9, "node": 3, "status": 1, "g1": 0.015, "b1": 0, "g0": 0.015, "b0": 0} + ] + } +})json"s; + +auto const update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "sym_load": [ + {"id": 7, "status": 1, "p_specified": 2500000} + ], + "asym_load": [ + {"id": 8, "status": 0} + ], + "shunt": [ + {"id": 9, "status": 0, "b1": 0.02, "b0": 0.02} + ], + "source": [ + {"id": 10, "status": 1, "u_ref": 0.84} + ], + "link": [ + {"id": 5, "from_status": 1, "to_status": 0} + ] + } + ] +})json"; + struct State { std::vector node_id{1, 2, 3}; std::vector node_u_rated{10e3, 10e3, 10e3}; @@ -416,65 +485,77 @@ struct State { } // namespace TEST_CASE("API model - all updates") { - using namespace std::string_literals; + // TODO(mgovers): remove + // State const state; + // auto const input_dataset = state.get_input_dataset(); + // Serializer serializer{state.get_input_dataset(), PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + auto const owning_input_dataset = load_dataset(state_json); + auto const& input_dataset = owning_input_dataset.dataset; - 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}; - // 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 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}; - 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", 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()); - 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 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 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}; + // 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", 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()); + // 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()); + + // TODO(mgovers): remove + // Serializer serializer{update_data, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + auto const owning_update_dataset = load_dataset(update_json); + auto const& update_data = owning_update_dataset.dataset; auto const output_dataset_type = "sym_output"s; for (Idx comp_type_idx = 0; comp_type_idx < input_info.n_components(); ++comp_type_idx) { From df9878c7b2749473f19ef3c1312d1ded0a608075 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 5 Dec 2024 17:49:59 +0100 Subject: [PATCH 04/44] migrate update of update with alternating compute mode Signed-off-by: Martijn Govers --- .../test_api_model_update.cpp | 90 ++++++++++++------- 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index cb21f3dbe..9e031d90b 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -360,7 +360,27 @@ auto const update_json = R"json({ ] } ] -})json"; +})json"s; + +auto const update_vector_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "sym_load": [ + {"id": 7, "status": 1, "p_specified": 2500000} + ], + "asym_load": [ + {"id": 8, "status": 0} + ], + "shunt": [ + {"id": 9, "status": 0, "b1": 0.02, "b0": 0.02} + ] + } + ] +})json"s; struct State { std::vector node_id{1, 2, 3}; @@ -605,8 +625,9 @@ TEST_CASE("API model - all updates") { } TEST_CASE("API model - updates w/ alternating compute mode") { - State const state; - auto const input_dataset = state.get_input_dataset(); + auto const owning_input_dataset = load_dataset(state_json); + auto const& input_dataset = owning_input_dataset.dataset; + auto model = Model{50.0, input_dataset}; auto const check_sym = [&model] { @@ -688,34 +709,41 @@ TEST_CASE("API model - updates w/ alternating compute mode") { 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", 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()); - 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 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", 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()); + // 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()); + + // // TODO(mgovers): remove + // Serializer serializer{update_data, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + auto const owning_update_dataset = load_dataset(update_vector_json); + auto const& update_data = owning_update_dataset.dataset; // This will lead to no topo change but param change model.update(update_data); From c585b25b9cc683b420c7fedd64457f29b64d4d42 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 9 Dec 2024 17:37:51 +0100 Subject: [PATCH 05/44] migrate incomplete update tests to JSON input Signed-off-by: Martijn Govers --- .../test_api_model_update.cpp | 179 ++++++++++++++---- 1 file changed, 138 insertions(+), 41 deletions(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 9e031d90b..357c4871f 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -764,6 +764,81 @@ TEST_CASE("API model - updates w/ alternating compute mode") { } namespace { +auto const incomplete_state_json = R"json({ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 1, "u_rated": 10000}, + {"id": 2, "u_rated": 10000}, + {"id": 3, "u_rated": 10000} + ], + "line": [ + {"id": 4, "from_node": 1, "to_node": 2, "from_status": 1, "to_status": 1, "r1": 10, "x1": 0, "c1": 0, "tan1": 0, "r0": 10, "x0": 0, "c0": 0, "tan0": 0, "i_n": 1000} + ], + "link": [ + {"id": 5, "from_node": 2, "to_node": 3, "from_status": 1, "to_status": 1} + ], + "source": [ + {"id": 6, "node": 1, "status": 1, "sk": 1000000000000}, + {"id": 10, "node": 3, "status": 0, "sk": 1000000000000} + ], + "sym_load": [ + {"id": 7, "node": 3, "status": 1, "type": 1, "q_specified": 0} + ], + "asym_load": [ + {"id": 8, "node": 3, "status": 1, "type": 1, "q_specified": [0, 0, 0]} + ], + "shunt": [ + {"id": 9, "node": 3, "status": 1, "g1": 0.015, "b1": 0, "g0": 0.015, "b0": 0} + ] + } +})json"s; + +auto const incomplete_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "source": [ + {"id": 6}, + {"id": 10} + ], + "sym_load": [ + {"id": 7} + ], + "asym_load": [ + {"id": 8} + ] + } + ] +})json"s; + +auto const complete_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "source": [ + {"id": 6, "u_ref": 1.05}, + {"id": 10, "u_ref": 1.05, "u_ref_angle": 0} + ], + "sym_load": [ + {"id": 7, "p_specified": 500000} + ], + "asym_load": [ + {"id": 8, "p_specified": [166666.6666666667, 166666.6666666667, 166666.6666666667]} + ] + } + ] +})json"s; + auto get_incomplete_state() -> State { State result; @@ -777,10 +852,20 @@ auto get_incomplete_state() -> State { } // namespace TEST_CASE("API model - incomplete input") { - State const complete_state; - State const incomplete_state = get_incomplete_state(); + State const complete_state; // TODO(mgovers): also migrate + // State const incomplete_state = get_incomplete_state(); - auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; + auto const owning_input_dataset = load_dataset(incomplete_state_json); + auto const& incomplete_input_data = owning_input_dataset.dataset; + + // // TODO(mgovers): remove + // Serializer serializer{update_data, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + // auto const owning_update_dataset = load_dataset(update_vector_json); + // auto const& update_data = owning_update_dataset.dataset; + + auto test_model = Model{50.0, incomplete_input_data}; for (auto symmetry : {PGM_symmetric, PGM_asymmetric}) { CAPTURE(symmetry); @@ -817,25 +902,31 @@ TEST_CASE("API model - incomplete input") { } } SUBCASE("Incomplete update dataset") { - DatasetConst incomplete_update_data{"update", true, 1}; - 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", 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", 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()); + // DatasetConst incomplete_update_data{"update", true, 1}; + // 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", 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", 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()); + // // TODO(mgovers): remove + // Serializer serializer{incomplete_update_data, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + auto const owning_update_dataset = load_dataset(incomplete_update_json); + auto const& incomplete_update_data = owning_update_dataset.dataset; SUBCASE("Single update") { CHECK_NOTHROW(test_model.update(incomplete_update_data)); @@ -850,25 +941,31 @@ TEST_CASE("API model - incomplete input") { } } SUBCASE("Complete update dataset") { - DatasetConst complete_update_data{"update", true, 1}; - 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", 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", 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()); + // DatasetConst complete_update_data{"update", true, 1}; + // 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", 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", 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()); + // // // TODO(mgovers): remove + // Serializer serializer{complete_update_data, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + auto const owning_update_dataset = load_dataset(complete_update_json); + auto const& complete_update_data = owning_update_dataset.dataset; auto ref_model = Model{50.0, complete_state.get_input_dataset()}; Buffer ref_node_output(node_output_meta, std::ssize(complete_state.node_id)); From e15d5fdb2628bb56b9edc0c416e7f87fd7bf6f37 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 9 Dec 2024 17:49:21 +0100 Subject: [PATCH 06/44] migrate incomplete followed by complete update tests to JSON input Signed-off-by: Martijn Govers --- .../test_api_model_update.cpp | 170 +++++++++++++----- 1 file changed, 124 insertions(+), 46 deletions(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 357c4871f..12a1f3d52 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -1013,12 +1013,76 @@ TEST_CASE("API model - incomplete input") { } } +auto const mixed_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "source": [ + {"id": 6, "status": 1}, + {"id": 10, "status": 1} + ], + "sym_load": [ + {"id": 7, "status": 1, "q_specified": 1} + ], + "asym_load": [ + {"id": 8, "status": 1, "q_specified": [1, 1, 1]} + ] + }, + { + "source": [ + {"id": 6, "status": 1, "u_ref": 1.05}, + {"id": 10, "status": 1, "u_ref": 1.05, "u_ref_angle": 0} + ], + "sym_load": [ + {"id": 7, "status": 0, "p_specified": 500000} + ], + "asym_load": [ + {"id": 8, "status": 0, "p_specified": [166666.6666666667, 166666.6666666667, 166666.6666666667]} + ] + } + ] +})json"s; + +auto const second_scenario_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "source": [ + {"id": 6, "status": 1, "u_ref": 1.05}, + {"id": 10, "status": 1, "u_ref": 1.05, "u_ref_angle": 0} + ], + "sym_load": [ + {"id": 7, "status": 1, "p_specified": 500000} + ], + "asym_load": [ + {"id": 8, "status": 1, "p_specified": [null, null, 166666.6666666667], "q_specified": [1, 1, null]} + ] + } + ] +})json"s; + 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()}; + // TODO(mgovers): remove + // auto ref_model = Model{50.0, complete_state.get_input_dataset()}; + // auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; + + auto const complete_owning_input_dataset = load_dataset(state_json); + auto const incomplete_owning_input_dataset = load_dataset(incomplete_state_json); + + auto const& complete_input_data = complete_owning_input_dataset.dataset; + auto const& incomplete_input_data = incomplete_owning_input_dataset.dataset; + + auto ref_model = Model{50.0, complete_input_data}; + auto test_model = Model{50.0, incomplete_input_data}; constexpr Idx batch_size = 2; auto const n_nodes = static_cast(complete_state.node_id.size()); @@ -1042,50 +1106,64 @@ TEST_CASE("API model - Incomplete scenario update followed by complete") { 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); + // 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()); + + // // TODO(mgovers): remove + // Serializer serializer{mixed_update_data, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + auto const mixed_owning_update_dataset = load_dataset(mixed_update_json); + auto const& mixed_update_data = mixed_owning_update_dataset.dataset; + + // 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); + + // // TODO(mgovers): remove + // Serializer serializer{second_scenario_update_data, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + auto const second_scenario_owning_update_dataset = load_dataset(second_scenario_update_json); + auto const& second_scenario_update_data = second_scenario_owning_update_dataset.dataset; for (auto symmetry : {PGM_symmetric, PGM_asymmetric}) { CAPTURE(symmetry); From 4ad1c3f86ce52c8a0880d32d4897ba270eb5c17f Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 10 Dec 2024 09:43:46 +0100 Subject: [PATCH 07/44] more migrations Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 10 +++ .../test_api_model_update.cpp | 83 ++----------------- 2 files changed, 15 insertions(+), 78 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 8a7f892f0..66ce76bef 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 @@ -43,6 +43,16 @@ class DatasetInfo { return handle_.call_with(PGM_dataset_info_total_elements, info_, component_idx); } + Idx component_idx(std::string_view component) const { + Idx const n_comp = n_components(); + for (Idx idx = 0; idx < n_comp; ++idx) { + if (component_name(idx) == component) { + return idx; + } + } + return -1; + } + private: Handle handle_{}; RawDatasetInfo const* info_; diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 12a1f3d52..3283f82cf 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -1071,10 +1071,6 @@ TEST_CASE("API model - Incomplete scenario update followed by complete") { State const complete_state; State const incomplete_state = get_incomplete_state(); - // TODO(mgovers): remove - // auto ref_model = Model{50.0, complete_state.get_input_dataset()}; - // auto test_model = Model{50.0, incomplete_state.get_input_dataset()}; - auto const complete_owning_input_dataset = load_dataset(state_json); auto const incomplete_owning_input_dataset = load_dataset(incomplete_state_json); @@ -1084,83 +1080,14 @@ TEST_CASE("API model - Incomplete scenario update followed by complete") { auto ref_model = Model{50.0, complete_input_data}; auto test_model = Model{50.0, incomplete_input_data}; - constexpr Idx batch_size = 2; - 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}; - 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()); - - // // TODO(mgovers): remove - // Serializer serializer{mixed_update_data, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); + auto const& input_info = complete_input_data.get_info(); + auto const n_nodes = input_info.component_elements_per_scenario(input_info.component_idx("node")); + REQUIRE(n_nodes == 3); auto const mixed_owning_update_dataset = load_dataset(mixed_update_json); auto const& mixed_update_data = mixed_owning_update_dataset.dataset; - - // 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); - - // // TODO(mgovers): remove - // Serializer serializer{second_scenario_update_data, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); + auto const batch_size = mixed_update_data.get_info().batch_size(); + REQUIRE(batch_size == 2); auto const second_scenario_owning_update_dataset = load_dataset(second_scenario_update_json); auto const& second_scenario_update_data = second_scenario_owning_update_dataset.dataset; From 0ccb71fc55ccb51b688ee232a74c19f85478317e Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 10 Dec 2024 09:55:42 +0100 Subject: [PATCH 08/44] cleanup Signed-off-by: Martijn Govers --- .../test_api_model_update.cpp | 294 +----------------- 1 file changed, 11 insertions(+), 283 deletions(-) diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 3283f82cf..e9b2391ff 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -381,133 +381,10 @@ auto const update_vector_json = R"json({ } ] })json"s; - -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", 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()); - result.add_attribute_buffer("node", "u_rated", node_u_rated.data()); - - 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()); - 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", 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", 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()); - 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", 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()); - 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", 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()); - 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", 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()); - 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") { // TODO(mgovers): remove - // State const state; - // auto const input_dataset = state.get_input_dataset(); // Serializer serializer{state.get_input_dataset(), PGM_json}; // auto const str = serializer.get_to_zero_terminated_string(0, 2); @@ -517,59 +394,6 @@ 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 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}; - // 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", 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()); - // 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()); - // TODO(mgovers): remove // Serializer serializer{update_data, PGM_json}; // auto const str = serializer.get_to_zero_terminated_string(0, 2); @@ -709,39 +533,6 @@ TEST_CASE("API model - updates w/ alternating compute mode") { 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", 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()); - // 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()); - - // // TODO(mgovers): remove - // Serializer serializer{update_data, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - auto const owning_update_dataset = load_dataset(update_vector_json); auto const& update_data = owning_update_dataset.dataset; @@ -838,32 +629,18 @@ auto const complete_update_json = R"json({ } ] })json"s; - -auto get_incomplete_state() -> State { - State result; - - 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; // TODO(mgovers): also migrate - // State const incomplete_state = get_incomplete_state(); - - auto const owning_input_dataset = load_dataset(incomplete_state_json); - auto const& incomplete_input_data = owning_input_dataset.dataset; + auto const complete_owning_input_dataset = load_dataset(state_json); + auto const& complete_input_data = complete_owning_input_dataset.dataset; - // // TODO(mgovers): remove - // Serializer serializer{update_data, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); + auto const incomplete_owning_input_dataset = load_dataset(incomplete_state_json); + auto const& incomplete_input_data = incomplete_owning_input_dataset.dataset; - // auto const owning_update_dataset = load_dataset(update_vector_json); - // auto const& update_data = owning_update_dataset.dataset; + auto const& input_info = complete_input_data.get_info(); + auto const n_nodes = input_info.component_elements_per_scenario(input_info.component_idx("node")); + REQUIRE(n_nodes == 3); auto test_model = Model{50.0, incomplete_input_data}; @@ -876,7 +653,7 @@ TEST_CASE("API model - incomplete input") { SUBCASE(calculation_symmetry.c_str()) { auto const* node_output_meta = MetaData::get_component_by_name(output_type, "node"); - Buffer test_node_output(node_output_meta, std::ssize(complete_state.node_id)); + Buffer test_node_output(node_output_meta, n_nodes); DatasetMutable test_result_data{output_type, true, 1}; test_result_data.add_buffer("node", test_node_output.size(), test_node_output.size(), nullptr, test_node_output); @@ -902,29 +679,6 @@ TEST_CASE("API model - incomplete input") { } } SUBCASE("Incomplete update dataset") { - // DatasetConst incomplete_update_data{"update", true, 1}; - // 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", 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", 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()); - // // TODO(mgovers): remove - // Serializer serializer{incomplete_update_data, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - auto const owning_update_dataset = load_dataset(incomplete_update_json); auto const& incomplete_update_data = owning_update_dataset.dataset; @@ -941,34 +695,11 @@ TEST_CASE("API model - incomplete input") { } } SUBCASE("Complete update dataset") { - // DatasetConst complete_update_data{"update", true, 1}; - // 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", 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", 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()); - // // // TODO(mgovers): remove - // Serializer serializer{complete_update_data, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - auto const owning_update_dataset = load_dataset(complete_update_json); auto const& complete_update_data = owning_update_dataset.dataset; - auto ref_model = Model{50.0, complete_state.get_input_dataset()}; - Buffer ref_node_output(node_output_meta, std::ssize(complete_state.node_id)); + auto ref_model = Model{50.0, complete_input_data}; + Buffer ref_node_output(node_output_meta, n_nodes); DatasetMutable ref_result_data{output_type, true, 1}; ref_result_data.add_buffer("node", ref_node_output.size(), ref_node_output.size(), nullptr, ref_node_output); @@ -985,7 +716,7 @@ TEST_CASE("API model - incomplete input") { complete_update_data)); } - for (Idx node_idx = 0; node_idx < std::ssize(complete_state.node_id); ++node_idx) { + for (Idx node_idx = 0; node_idx < n_nodes; ++node_idx) { CAPTURE(node_idx); for (Idx attr_idx = 0; attr_idx < MetaData::n_attributes(node_output_meta); ++attr_idx) { @@ -1068,9 +799,6 @@ auto const second_scenario_update_json = R"json({ })json"s; TEST_CASE("API model - Incomplete scenario update followed by complete") { - State const complete_state; - State const incomplete_state = get_incomplete_state(); - auto const complete_owning_input_dataset = load_dataset(state_json); auto const incomplete_owning_input_dataset = load_dataset(incomplete_state_json); From 9d53b55d2bb4ecf5612e715ab5759b8f192d0852 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Wed, 11 Dec 2024 09:43:16 +0100 Subject: [PATCH 09/44] migrate input + update data of test API model Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 1 + .../power_grid_model_cpp/serialization.hpp | 1 + .../test_math_solver_common.hpp | 6 + tests/cpp_unit_tests/test_math_solver_pf.hpp | 6 + tests/cpp_unit_tests/test_math_solver_se.hpp | 6 + tests/cpp_unit_tests/test_optimizer.hpp | 6 + tests/native_api_tests/load_dataset.hpp | 21 + tests/native_api_tests/test_api_model.cpp | 362 ++++++++++++++---- .../test_api_model_update.cpp | 12 +- 9 files changed, 347 insertions(+), 74 deletions(-) create mode 100644 tests/native_api_tests/load_dataset.hpp 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 66ce76bef..021c15fbe 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 @@ -11,6 +11,7 @@ #include "handle.hpp" #include "power_grid_model_c/dataset.h" + namespace power_grid_model_cpp { class DatasetInfo { diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp index 185c768ec..e8e6ad53b 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp @@ -9,6 +9,7 @@ #include "basics.hpp" #include "dataset.hpp" #include "handle.hpp" +#include "meta_data.hpp" #include "power_grid_model_c/serialization.h" diff --git a/tests/cpp_unit_tests/test_math_solver_common.hpp b/tests/cpp_unit_tests/test_math_solver_common.hpp index 5f07e1bc1..37f027b31 100644 --- a/tests/cpp_unit_tests/test_math_solver_common.hpp +++ b/tests/cpp_unit_tests/test_math_solver_common.hpp @@ -4,6 +4,10 @@ // In this unit test the powerflow solvers are tested +#pragma once +#ifndef POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_COMMON_HPP +#define POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_COMMON_HPP + #include #include #include @@ -265,3 +269,5 @@ template struct SteadyStateSolverTestGrid { }; } // namespace power_grid_model + +#endif // POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_COMMON_HPP diff --git a/tests/cpp_unit_tests/test_math_solver_pf.hpp b/tests/cpp_unit_tests/test_math_solver_pf.hpp index 1939cff45..ffe927514 100644 --- a/tests/cpp_unit_tests/test_math_solver_pf.hpp +++ b/tests/cpp_unit_tests/test_math_solver_pf.hpp @@ -4,6 +4,10 @@ // In this unit test the powerflow solvers are tested +#pragma once +#ifndef POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_PF_HPP +#define POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_PF_HPP + #include "test_math_solver_common.hpp" #include @@ -142,3 +146,5 @@ TEST_CASE_TEMPLATE_DEFINE("Test math solver - PF", SolverType, test_math_solver_ } } // namespace power_grid_model + +#endif // POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_PF_HPP diff --git a/tests/cpp_unit_tests/test_math_solver_se.hpp b/tests/cpp_unit_tests/test_math_solver_se.hpp index 93c4f65fe..dfbbceb5a 100644 --- a/tests/cpp_unit_tests/test_math_solver_se.hpp +++ b/tests/cpp_unit_tests/test_math_solver_se.hpp @@ -4,6 +4,10 @@ // In this unit test the powerflow solvers are tested +#pragma once +#ifndef POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_SE_HPP +#define POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_SE_HPP + #include "test_math_solver_common.hpp" #include @@ -516,3 +520,5 @@ TEST_CASE_TEMPLATE_DEFINE("Test math solver - SE, measurements", SolverType, tes } } // namespace power_grid_model + +#endif // POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_SE_HPP diff --git a/tests/cpp_unit_tests/test_optimizer.hpp b/tests/cpp_unit_tests/test_optimizer.hpp index f640d145e..c941ef8d3 100644 --- a/tests/cpp_unit_tests/test_optimizer.hpp +++ b/tests/cpp_unit_tests/test_optimizer.hpp @@ -2,6 +2,10 @@ // // SPDX-License-Identifier: MPL-2.0 +#pragma once +#ifndef POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_OPTIMIZER_HPP +#define POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_OPTIMIZER_HPP + #include #include #include @@ -246,3 +250,5 @@ template <> struct get_attributes_list { }; } // namespace meta_data } // namespace power_grid_model + +#endif // POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_OPTIMIZER_HPP diff --git a/tests/native_api_tests/load_dataset.hpp b/tests/native_api_tests/load_dataset.hpp new file mode 100644 index 000000000..e118475a4 --- /dev/null +++ b/tests/native_api_tests/load_dataset.hpp @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Contributors to the Power Grid Model project +// +// SPDX-License-Identifier: MPL-2.0 + +#pragma once +#ifndef POWER_GRID_MODEL_TESTS_NATIVE_API_TESTS_LOAD_DATASET_HPP +#define POWER_GRID_MODEL_TESTS_NATIVE_API_TESTS_LOAD_DATASET_HPP + +#include + +namespace power_grid_model_cpp_test { +inline power_grid_model_cpp::OwningDatasetConst load_dataset(std::string const& json_string) { + power_grid_model_cpp::Deserializer deserializer{json_string, PGM_json}; + auto& writable_dataset = deserializer.get_dataset(); + auto owning_dataset = power_grid_model_cpp::create_owning_dataset(writable_dataset); + deserializer.parse_to_buffer(); + return owning_dataset; +} +} // namespace power_grid_model_cpp_test + +#endif // POWER_GRID_MODEL_TESTS_NATIVE_API_TESTS_LOAD_DATASET_HPP diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index 9808715dd..810847f16 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -2,6 +2,8 @@ // // SPDX-License-Identifier: MPL-2.0 +#include "load_dataset.hpp" + #include "power_grid_model_cpp.hpp" #include @@ -58,6 +60,9 @@ Dataset created with the following buffers: namespace power_grid_model_cpp { namespace { +using namespace std::string_literals; +using power_grid_model_cpp_test::load_dataset; + enum class MeasuredTerminalType : IntS { branch_from = 0, branch_to = 1, @@ -89,6 +94,180 @@ void check_throws_with(Func&& func, PGM_ErrorCode const& reference_error, std::s check_exception(e, reference_error, reference_err_msg); } } + +auto const state_json = R"json({ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "sym_load": [ + {"id": 2, "node": 0, "status": 1, "type": 2, "p_specified": 0, "q_specified": 500} + ], + "source": [ + {"id": 1, "node": 0, "status": 1, "u_ref": 1, "sk": 1000, "rx_ratio": 0} + ], + "node": [ + {"id": 0, "u_rated": 100}, + {"id": 4, "u_rated": 100} + ], + "line": [ + {"id": 5, "from_node": 0, "to_node": 4, "from_status": 0, "to_status": 1}, + {"id": 6, "from_node": 4, "to_node": 0, "from_status": 0, "to_status": 0} + ] + } +})json"s; + +auto const bad_load_id_state_json = R"json({ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "sym_load": [ + {"id": 0, "node": 0, "status": 1, "type": 2, "p_specified": 0, "q_specified": 500} + ], + "source": [ + {"id": 1, "node": 0, "status": 1, "u_ref": 1, "sk": 1000, "rx_ratio": 0} + ], + "node": [ + {"id": 0, "u_rated": 100}, + {"id": 4, "u_rated": 100} + ], + "line": [ + {"id": 5, "from_node": 0, "to_node": 4, "from_status": 0, "to_status": 1}, + {"id": 6, "from_node": 4, "to_node": 0, "from_status": 0, "to_status": 0} + ] + } +})json"s; + +auto const single_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": false, + "attributes": {}, + "data": { + "source": [ + {"id": 1, "u_ref": 0.5} + ], + "sym_load": [ + {"id": 2, "q_specified": 100} + ], + "line": [ + {"id": 5, "from_status": 0, "to_status": 1}, + {"id": 6, "from_status": 0, "to_status": 0} + ] + } +})json"s; + +auto const batch_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "source": [ + {"id": 1, "u_ref": 0.5} + ], + "sym_load": [ + {"id": 2, "q_specified": 100} + ], + "line": [ + {"id": 5, "from_status": 0, "to_status": 1}, + {"id": 6, "from_status": 0, "to_status": 0} + ] + }, + { + "sym_load": [ + {"id": 2, "q_specified": 300} + ], + "line": [ + {"id": 5, "from_status": 0, "to_status": 0}, + {"id": 6, "from_status": 0, "to_status": 0} + ] + } + ] +})json"s; + +auto const bad_source_id_single_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": false, + "attributes": {}, + "data": { + "source": [ + {"id": 99, "u_ref": 0.5} + ], + "sym_load": [ + {"id": 2, "q_specified": 100} + ], + "line": [ + {"id": 5, "from_status": 0, "to_status": 1}, + {"id": 6, "from_status": 0, "to_status": 0} + ] + } +})json"s; + +auto const bad_load_id_batch_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "source": [ + {"id": 1, "u_ref": 0.5} + ], + "sym_load": [ + {"id": 2, "q_specified": 100} + ], + "line": [ + {"id": 99, "from_status": 0, "to_status": 1}, + {"id": 999, "from_status": 0, "to_status": 0} + ] + }, + { + "sym_load": [ + {"id": 2, "q_specified": 300} + ], + "line": [ + {"id": 9999, "from_status": 0, "to_status": 0}, + {"id": 99999, "from_status": 0, "to_status": 0} + ] + } + ] +})json"s; + +auto const bad_line_id_batch_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "source": [ + {"id": 1, "u_ref": 0.5} + ], + "sym_load": [ + {"id": 2, "q_specified": 100} + ], + "line": [ + {"id": 5, "from_status": 0, "to_status": 1}, + {"id": 6, "from_status": 0, "to_status": 0} + ] + }, + { + "sym_load": [ + {"id": 999, "q_specified": 300} + ], + "line": [ + {"id": 5, "from_status": 0, "to_status": 0}, + {"id": 6, "from_status": 0, "to_status": 0} + ] + } + ] +})json"s; } // namespace TEST_CASE("API Model") { @@ -96,9 +275,6 @@ TEST_CASE("API Model") { Options options{}; - // input data - DatasetConst input_dataset{"input", false, 1}; - // node buffer std::vector const node_id{0, 4}; std::vector const node_u_rated{100.0, 100.0}; @@ -146,6 +322,9 @@ TEST_CASE("API Model") { load_buffer.set_value(PGM_def_input_sym_load_p_specified, &load_p_specified, -1); load_buffer.set_value(PGM_def_input_sym_load_q_specified, &load_q_specified, -1); + // input data + DatasetConst input_dataset{"input", false, 1}; + // add buffers - row input_dataset.add_buffer("sym_load", 1, 1, nullptr, load_buffer); input_dataset.add_buffer("source", 1, 1, nullptr, source_buffer); @@ -161,6 +340,13 @@ TEST_CASE("API Model") { input_dataset.add_attribute_buffer("line", "from_status", line_from_status.data()); input_dataset.add_attribute_buffer("line", "to_status", line_to_status.data()); + // // TODO(mgovers): remove + // Serializer serializer{input_dataset, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + // auto const owning_input_dataset = load_dataset(state_json); + // auto const& input_dataset = owning_input_dataset.dataset; + // output data Buffer node_output{PGM_def_sym_output_node, 2}; node_output.set_nan(); @@ -182,43 +368,53 @@ TEST_CASE("API Model") { std::vector batch_node_result_u_pu(4); std::vector batch_node_result_u_angle(4); - // update data - ID const source_update_id = 1; - int8_t const source_update_status = std::numeric_limits::min(); - double const source_update_u_ref = 0.5; - double const source_update_u_ref_angle = std::numeric_limits::quiet_NaN(); - Buffer source_update_buffer{PGM_def_update_source, 1}; - source_update_buffer.set_nan(); - source_update_buffer.set_value(PGM_def_update_source_id, &source_update_id, 0, -1); - source_update_buffer.set_value(PGM_def_update_source_status, &source_update_status, 0, -1); - source_update_buffer.set_value(PGM_def_update_source_u_ref, &source_update_u_ref, 0, -1); - source_update_buffer.set_value(PGM_def_update_source_u_ref_angle, &source_update_u_ref_angle, 0, -1); - std::array source_update_indptr{0, 1, 1}; - - std::vector load_updates_id = {2, 2}; - std::vector load_updates_q_specified = {100.0, 300.0}; - Buffer load_updates_buffer{PGM_def_update_sym_load, 2}; - // set nan twice with offset - load_updates_buffer.set_nan(0); - load_updates_buffer.set_nan(1); - load_updates_buffer.set_value(PGM_def_update_sym_load_id, load_updates_id.data(), -1); - 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", 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", 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); - batch_update_dataset.add_attribute_buffer("line", "id", batch_line_id.data()); - batch_update_dataset.add_attribute_buffer("line", "from_status", batch_line_from_status.data()); - batch_update_dataset.add_attribute_buffer("line", "to_status", batch_line_to_status.data()); + // // update data + // ID const source_update_id = 1; + // int8_t const source_update_status = std::numeric_limits::min(); + // double const source_update_u_ref = 0.5; + // double const source_update_u_ref_angle = std::numeric_limits::quiet_NaN(); + // Buffer source_update_buffer{PGM_def_update_source, 1}; + // source_update_buffer.set_nan(); + // source_update_buffer.set_value(PGM_def_update_source_id, &source_update_id, 0, -1); + // source_update_buffer.set_value(PGM_def_update_source_status, &source_update_status, 0, -1); + // source_update_buffer.set_value(PGM_def_update_source_u_ref, &source_update_u_ref, 0, -1); + // source_update_buffer.set_value(PGM_def_update_source_u_ref_angle, &source_update_u_ref_angle, 0, -1); + // std::array source_update_indptr{0, 1, 1}; + + // std::vector load_updates_id = {2, 2}; + // std::vector load_updates_q_specified = {100.0, 300.0}; + // Buffer load_updates_buffer{PGM_def_update_sym_load, 2}; + // // set nan twice with offset + // load_updates_buffer.set_nan(0); + // load_updates_buffer.set_nan(1); + // load_updates_buffer.set_value(PGM_def_update_sym_load_id, load_updates_id.data(), -1); + // 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", 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", 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); + // batch_update_dataset.add_attribute_buffer("line", "id", batch_line_id.data()); + // batch_update_dataset.add_attribute_buffer("line", "from_status", batch_line_from_status.data()); + // batch_update_dataset.add_attribute_buffer("line", "to_status", batch_line_to_status.data()); + + // // TODO(mgovers): remove + // Serializer serializer{batch_update_dataset, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + auto const single_owning_update_dataset = load_dataset(single_update_json); + auto const& single_update_dataset = single_owning_update_dataset.dataset; + + auto const batch_owning_update_dataset = load_dataset(batch_update_json); + auto const& batch_update_dataset = batch_owning_update_dataset.dataset; // create model Model model{50.0, input_dataset}; @@ -343,21 +539,42 @@ TEST_CASE("API Model") { SUBCASE("Input error handling") { SUBCASE("Construction error") { - ID const bad_load_id = 0; - ID const good_source_update_id = 1; - load_buffer.set_value(PGM_def_input_sym_load_id, &bad_load_id, -1); - source_update_buffer.set_value(PGM_def_update_source_id, &good_source_update_id, 0, -1); + // ID const bad_load_id = 0; + // ID const good_source_update_id = 1; + // load_buffer.set_value(PGM_def_input_sym_load_id, &bad_load_id, -1); + // source_update_buffer.set_value(PGM_def_update_source_id, &good_source_update_id, 0, -1); + + // // TODO(mgovers): remove + // Serializer serializer{input_dataset, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); - auto const wrong_model_lambda = [&input_dataset]() { Model const wrong_model{50.0, input_dataset}; }; + auto const bad_owning_input_dataset = load_dataset(bad_load_id_state_json); + auto const& bad_input_dataset = bad_owning_input_dataset.dataset; - check_throws_with(wrong_model_lambda, PGM_regular_error, "Conflicting id detected:"s); + auto const bad_model_lambda = [&bad_input_dataset]() { Model const wrong_model{50.0, bad_input_dataset}; }; + + check_throws_with(bad_model_lambda, PGM_regular_error, "Conflicting id detected:"s); } SUBCASE("Update error") { - ID const good_load_id = 2; - ID const bad_source_update_id = 99; - load_buffer.set_value(PGM_def_input_sym_load_id, &good_load_id, -1); - source_update_buffer.set_value(PGM_def_update_source_id, &bad_source_update_id, 0, -1); + // ID const good_load_id = 2; + // ID const bad_source_update_id = 99; + // load_buffer.set_value(PGM_def_input_sym_load_id, &good_load_id, -1); + // source_update_buffer.set_value(PGM_def_update_source_id, &bad_source_update_id, 0, -1); + + // // TODO(mgovers): remove + // Serializer serializer{input_dataset, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + // auto const owning_input_dataset = load_dataset(bad_source_update_load_id_state_json); + // auto const& input_dataset = owning_input_dataset.dataset; + + // // TODO(mgovers): remove + // Serializer serializer{single_update_dataset, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + auto const bad_single_owning_update_dataset = load_dataset(bad_source_id_single_update_json); + auto const& single_update_dataset = bad_single_owning_update_dataset.dataset; auto const bad_update_lambda = [&model, &single_update_dataset]() { model.update(single_update_dataset); }; @@ -365,17 +582,24 @@ 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", 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); - bad_batch_update_dataset.add_buffer("line", 2, 4, nullptr, nullptr); // columnar input for line - std::vector const bad_batch_line_id{99, 999, 9999, 99999}; - bad_batch_update_dataset.add_attribute_buffer("line", "id", bad_batch_line_id.data()); - bad_batch_update_dataset.add_attribute_buffer("line", "from_status", batch_line_from_status.data()); - bad_batch_update_dataset.add_attribute_buffer("line", "to_status", batch_line_to_status.data()); + // 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", 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); + // bad_batch_update_dataset.add_buffer("line", 2, 4, nullptr, nullptr); // columnar input for line + // std::vector const bad_batch_line_id{99, 999, 9999, 99999}; + // bad_batch_update_dataset.add_attribute_buffer("line", "id", bad_batch_line_id.data()); + // bad_batch_update_dataset.add_attribute_buffer("line", "from_status", batch_line_from_status.data()); + // bad_batch_update_dataset.add_attribute_buffer("line", "to_status", batch_line_to_status.data()); + + // // TODO(mgovers): remove + // Serializer serializer{bad_batch_update_dataset, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + auto const bad_batch_owning_update_dataset = load_dataset(bad_load_id_batch_update_json); + auto const& bad_batch_update_dataset = bad_batch_owning_update_dataset.dataset; auto const bad_calc_with_update_lambda = [&model, &options, &batch_output_dataset, &bad_batch_update_dataset]() { @@ -427,10 +651,18 @@ TEST_CASE("API Model") { SUBCASE("Batch calculation error") { SUBCASE("Line bad line id") { - // wrong id - load_updates_id[1] = 999; - load_updates_buffer.set_value(PGM_def_update_sym_load_id, load_updates_id.data(), 1, -1); - // failed in batch 1 + // // wrong id + // load_updates_id[1] = 999; + // load_updates_buffer.set_value(PGM_def_update_sym_load_id, load_updates_id.data(), 1, -1); + + // // TODO(mgovers): remove + // Serializer serializer{batch_update_dataset, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + + auto const bad_batch_owning_update_dataset = load_dataset(bad_line_id_batch_update_json); + auto const& batch_update_dataset = bad_batch_owning_update_dataset.dataset; + + // failed in batch scenario 1 try { model.calculate(options, batch_output_dataset, batch_update_dataset); FAIL("Expected batch calculation error not thrown."); diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index e9b2391ff..0835502f3 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -2,10 +2,11 @@ // // SPDX-License-Identifier: MPL-2.0 +#include "load_dataset.hpp" + #include #include #include -#include #include #include @@ -83,6 +84,7 @@ TYPE_TO_STRING_AS("row_t, columnar_t, dense_t, invalid_id_t", TypeCombo); namespace power_grid_model_cpp { +using power_grid_model_cpp_test::load_dataset; /* @@ -286,14 +288,6 @@ Options get_default_options(PGM_SymmetryType calculation_symmetry, PGM_Calculati return opt; } -OwningDatasetConst load_dataset(std::string const& json_string) { - Deserializer deserializer{json_string, PGM_json}; - auto& writable_dataset = deserializer.get_dataset(); - auto owning_dataset = create_owning_dataset(writable_dataset); - deserializer.parse_to_buffer(); - return owning_dataset; -} - 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; From 5ab23205c96c624b1353abbae55064a40140ce94 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Wed, 11 Dec 2024 10:14:34 +0100 Subject: [PATCH 10/44] migrate some more test api model functions Signed-off-by: Martijn Govers --- tests/native_api_tests/test_api_model.cpp | 187 ++++++++++++++++------ 1 file changed, 137 insertions(+), 50 deletions(-) diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index 810847f16..2818b50c2 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -1047,52 +1047,102 @@ TEST_CASE("API Model") { } SUBCASE("Forbid link power measurements") { - // input data - DatasetConst input_dataset_se{"input", false, 1}; - auto const construct_model = [&input_dataset_se] { return Model{50.0, input_dataset_se}; }; + // // input data + // DatasetConst input_dataset_se{"input", false, 1}; + // auto const construct_model = [&input_dataset_se] { return Model{50.0, input_dataset_se}; }; - // node buffer - std::vector const node_id_se{1, 2}; - std::vector const node_u_rated_se{10000.0, 10000.0}; + // // node buffer + // std::vector const node_id_se{1, 2}; + // std::vector const node_u_rated_se{10000.0, 10000.0}; - // link buffer - std::vector const link_id_se{3}; - std::vector const link_from_node_se{1}; - std::vector const link_to_node_se{2}; + // // link buffer + // std::vector const link_id_se{3}; + // std::vector const link_from_node_se{1}; + // std::vector const link_to_node_se{2}; - // power sensor - std::vector const power_sensor_id_se{4}; - std::vector const power_sensor_measured_object_se{3}; - std::vector const power_sensor_measured_terminal_type_se{0}; + // // power sensor + // std::vector const power_sensor_id_se{4}; + // std::vector const power_sensor_measured_object_se{3}; + // std::vector const power_sensor_measured_terminal_type_se{0}; - input_dataset_se.add_buffer("node", 2, 2, nullptr, nullptr); - input_dataset_se.add_attribute_buffer("node", "id", node_id_se.data()); - input_dataset_se.add_attribute_buffer("node", "u_rated", node_u_rated_se.data()); + // input_dataset_se.add_buffer("node", 2, 2, nullptr, nullptr); + // input_dataset_se.add_attribute_buffer("node", "id", node_id_se.data()); + // input_dataset_se.add_attribute_buffer("node", "u_rated", node_u_rated_se.data()); - input_dataset_se.add_buffer("link", 1, 1, nullptr, nullptr); - input_dataset_se.add_attribute_buffer("link", "id", link_id_se.data()); - input_dataset_se.add_attribute_buffer("link", "from_node", link_from_node_se.data()); - input_dataset_se.add_attribute_buffer("link", "to_node", link_to_node_se.data()); + // input_dataset_se.add_buffer("link", 1, 1, nullptr, nullptr); + // input_dataset_se.add_attribute_buffer("link", "id", link_id_se.data()); + // input_dataset_se.add_attribute_buffer("link", "from_node", link_from_node_se.data()); + // input_dataset_se.add_attribute_buffer("link", "to_node", link_to_node_se.data()); SUBCASE("SymPowerSensor") { - input_dataset_se.add_buffer("sym_power_sensor", 1, 1, nullptr, nullptr); - input_dataset_se.add_attribute_buffer("sym_power_sensor", "id", power_sensor_id_se.data()); - input_dataset_se.add_attribute_buffer("sym_power_sensor", "measured_object", - power_sensor_measured_object_se.data()); - input_dataset_se.add_attribute_buffer("sym_power_sensor", "measured_terminal_type", - power_sensor_measured_terminal_type_se.data()); + // input_dataset_se.add_buffer("sym_power_sensor", 1, 1, nullptr, nullptr); + // input_dataset_se.add_attribute_buffer("sym_power_sensor", "id", power_sensor_id_se.data()); + // input_dataset_se.add_attribute_buffer("sym_power_sensor", "measured_object", + // power_sensor_measured_object_se.data()); + // input_dataset_se.add_attribute_buffer("sym_power_sensor", "measured_terminal_type", + // power_sensor_measured_terminal_type_se.data()); + + // // TODO(mgovers): remove + // Serializer serializer{input_dataset_se, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + auto const input_data_se_json = R"json({ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 1, "u_rated": 10000}, + {"id": 2, "u_rated": 10000} + ], + "link": [ + {"id": 3, "from_node": 1, "to_node": 2} + ], + "sym_power_sensor": [ + {"id": 4, "measured_object": 3, "measured_terminal_type": 0} + ] + } +})json"s; + + auto const owning_input_dataset_se = load_dataset(input_data_se_json); + auto const& input_dataset_se = owning_input_dataset_se.dataset; + + auto const construct_model = [&input_dataset_se] { return Model{50.0, input_dataset_se}; }; CHECK_THROWS_WITH_AS(construct_model(), "PowerSensor measurement is not supported for object of type Link", PowerGridRegularError); } SUBCASE("AsymPowerSensor") { - input_dataset_se.add_buffer("asym_power_sensor", 2, 2, nullptr, nullptr); - input_dataset_se.add_attribute_buffer("asym_power_sensor", "id", power_sensor_id_se.data()); - input_dataset_se.add_attribute_buffer("asym_power_sensor", "measured_object", - power_sensor_measured_object_se.data()); - input_dataset_se.add_attribute_buffer("asym_power_sensor", "measured_terminal_type", - power_sensor_measured_terminal_type_se.data()); + // input_dataset_se.add_buffer("asym_power_sensor", 2, 2, nullptr, nullptr); + // input_dataset_se.add_attribute_buffer("asym_power_sensor", "id", power_sensor_id_se.data()); + // input_dataset_se.add_attribute_buffer("asym_power_sensor", "measured_object", + // power_sensor_measured_object_se.data()); + // input_dataset_se.add_attribute_buffer("asym_power_sensor", "measured_terminal_type", + // power_sensor_measured_terminal_type_se.data()); + auto const input_data_se_json = R"json({ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 1, "u_rated": 10000}, + {"id": 2, "u_rated": 10000} + ], + "link": [ + {"id": 3, "from_node": 1, "to_node": 2} + ], + "asym_power_sensor": [ + {"id": 4, "measured_object": 3, "measured_terminal_type": 0} + ] + } +})json"s; + + auto const owning_input_dataset_se = load_dataset(input_data_se_json); + auto const& input_dataset_se = owning_input_dataset_se.dataset; + + auto const construct_model = [&input_dataset_se] { return Model{50.0, input_dataset_se}; }; CHECK_THROWS_WITH_AS(construct_model(), "PowerSensor measurement is not supported for object of type Link", PowerGridRegularError); @@ -1100,34 +1150,71 @@ TEST_CASE("API Model") { } SUBCASE("Test duplicated id") { - std::vector node_id_2{1, 1, 3}; - DatasetConst input_dataset_2{"input", false, 1}; + // std::vector node_id_2{1, 1, 3}; + // 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_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 const input_data_2_json = R"json({ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 1}, + {"id": 1}, + {"id": 3} + ] + } +})json"s; + + auto const owning_input_dataset_2 = load_dataset(input_data_2_json); + auto const& input_dataset_2 = owning_input_dataset_2.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_2{1, 2, 3}; - std::vector const node_u_rated_2{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}; + // std::vector link_id{5}; + // std::vector link_from_node{99}; + // std::vector link_to_node{3}; - DatasetConst input_dataset_2{"input", false, 1}; + // 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("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("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("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()); + + // // TODO(mgovers): remove + // Serializer serializer{input_dataset_2, PGM_json}; + // auto const str = serializer.get_to_zero_terminated_string(0, 2); + auto const input_data_2_json = R"json({ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 1, "u_rated": 10000}, + {"id": 2, "u_rated": 10000} + ], + "link": [ + {"id": 5, "from_node": 99, "to_node": 2} + ] + } +})json"s; + auto const owning_input_dataset_2 = load_dataset(input_data_2_json); + auto const& input_dataset_2 = owning_input_dataset_2.dataset; auto construct_model = [&] { Model{50.0, input_dataset_2}; }; CHECK_THROWS_WITH_AS(construct_model(), "The id cannot be found: 99\n", PowerGridRegularError); From 35eff1731c25396756187aea3c20b8348f01cd84 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Wed, 11 Dec 2024 10:18:55 +0100 Subject: [PATCH 11/44] remove unused code Signed-off-by: Martijn Govers --- tests/native_api_tests/test_api_model.cpp | 245 +--------------------- 1 file changed, 8 insertions(+), 237 deletions(-) diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index 2818b50c2..75421bfb3 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -275,77 +275,14 @@ TEST_CASE("API Model") { Options options{}; - // node buffer - std::vector const node_id{0, 4}; - std::vector const node_u_rated{100.0, 100.0}; - - // line buffer - std::vector const line_id{5, 6}; - std::vector const line_from_node{0, 4}; - std::vector const line_to_node{4, 0}; - std::vector const line_from_status{0, 1}; - std::vector const line_to_status{1, 0}; - std::vector const batch_line_id{5, 6, 5, 6}; - std::vector const batch_line_from_node{0, 4, 0, 4}; - std::vector const batch_line_to_node{4, 0, 4, 0}; - std::vector const batch_line_from_status{0, 1, 0, 1}; - std::vector const batch_line_to_status{1, 0, 1, 0}; - - // source buffer - ID const source_id = 1; - ID const source_node = 0; - int8_t const source_status = 1; - double const source_u_ref = 1.0; - double const source_sk = 1000.0; - double const source_rx_ratio = 0.0; - Buffer source_buffer{PGM_def_input_source, 1}; - source_buffer.set_nan(); - source_buffer.set_value(PGM_def_input_source_id, &source_id, -1); - source_buffer.set_value(PGM_def_input_source_node, &source_node, 0, sizeof(ID)); - source_buffer.set_value(PGM_def_input_source_status, &source_status, -1); - source_buffer.set_value(PGM_def_input_source_u_ref, &source_u_ref, -1); - source_buffer.set_value(PGM_def_input_source_sk, &source_sk, -1); - source_buffer.set_value(PGM_def_input_source_rx_ratio, &source_rx_ratio, -1); - - // load buffer - ID const load_id = 2; - ID const load_node = 0; - int8_t const load_status = 1; - int8_t const load_type = 2; - double const load_p_specified = 0.0; - double const load_q_specified = 500.0; - Buffer load_buffer{PGM_def_input_sym_load, 1}; - load_buffer.set_value(PGM_def_input_sym_load_id, &load_id, -1); - load_buffer.set_value(PGM_def_input_sym_load_node, &load_node, -1); - load_buffer.set_value(PGM_def_input_sym_load_status, &load_status, -1); - load_buffer.set_value(PGM_def_input_sym_load_type, &load_type, -1); - load_buffer.set_value(PGM_def_input_sym_load_p_specified, &load_p_specified, -1); - load_buffer.set_value(PGM_def_input_sym_load_q_specified, &load_q_specified, -1); - - // input data - DatasetConst input_dataset{"input", false, 1}; - - // add buffers - row - input_dataset.add_buffer("sym_load", 1, 1, nullptr, load_buffer); - input_dataset.add_buffer("source", 1, 1, nullptr, source_buffer); - - // add buffers - columnar - input_dataset.add_buffer("node", 2, 2, 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", 2, 2, 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_attribute_buffer("line", "from_status", line_from_status.data()); - input_dataset.add_attribute_buffer("line", "to_status", line_to_status.data()); - - // // TODO(mgovers): remove - // Serializer serializer{input_dataset, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - - // auto const owning_input_dataset = load_dataset(state_json); - // auto const& input_dataset = owning_input_dataset.dataset; + auto const owning_input_dataset = load_dataset(state_json); + auto const& input_dataset = owning_input_dataset.dataset; + + auto const single_owning_update_dataset = load_dataset(single_update_json); + auto const& single_update_dataset = single_owning_update_dataset.dataset; + + auto const batch_owning_update_dataset = load_dataset(batch_update_json); + auto const& batch_update_dataset = batch_owning_update_dataset.dataset; // output data Buffer node_output{PGM_def_sym_output_node, 2}; @@ -368,54 +305,6 @@ TEST_CASE("API Model") { std::vector batch_node_result_u_pu(4); std::vector batch_node_result_u_angle(4); - // // update data - // ID const source_update_id = 1; - // int8_t const source_update_status = std::numeric_limits::min(); - // double const source_update_u_ref = 0.5; - // double const source_update_u_ref_angle = std::numeric_limits::quiet_NaN(); - // Buffer source_update_buffer{PGM_def_update_source, 1}; - // source_update_buffer.set_nan(); - // source_update_buffer.set_value(PGM_def_update_source_id, &source_update_id, 0, -1); - // source_update_buffer.set_value(PGM_def_update_source_status, &source_update_status, 0, -1); - // source_update_buffer.set_value(PGM_def_update_source_u_ref, &source_update_u_ref, 0, -1); - // source_update_buffer.set_value(PGM_def_update_source_u_ref_angle, &source_update_u_ref_angle, 0, -1); - // std::array source_update_indptr{0, 1, 1}; - - // std::vector load_updates_id = {2, 2}; - // std::vector load_updates_q_specified = {100.0, 300.0}; - // Buffer load_updates_buffer{PGM_def_update_sym_load, 2}; - // // set nan twice with offset - // load_updates_buffer.set_nan(0); - // load_updates_buffer.set_nan(1); - // load_updates_buffer.set_value(PGM_def_update_sym_load_id, load_updates_id.data(), -1); - // 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", 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", 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); - // batch_update_dataset.add_attribute_buffer("line", "id", batch_line_id.data()); - // batch_update_dataset.add_attribute_buffer("line", "from_status", batch_line_from_status.data()); - // batch_update_dataset.add_attribute_buffer("line", "to_status", batch_line_to_status.data()); - - // // TODO(mgovers): remove - // Serializer serializer{batch_update_dataset, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - - auto const single_owning_update_dataset = load_dataset(single_update_json); - auto const& single_update_dataset = single_owning_update_dataset.dataset; - - auto const batch_owning_update_dataset = load_dataset(batch_update_json); - auto const& batch_update_dataset = batch_owning_update_dataset.dataset; - // create model Model model{50.0, input_dataset}; @@ -539,15 +428,6 @@ TEST_CASE("API Model") { SUBCASE("Input error handling") { SUBCASE("Construction error") { - // ID const bad_load_id = 0; - // ID const good_source_update_id = 1; - // load_buffer.set_value(PGM_def_input_sym_load_id, &bad_load_id, -1); - // source_update_buffer.set_value(PGM_def_update_source_id, &good_source_update_id, 0, -1); - - // // TODO(mgovers): remove - // Serializer serializer{input_dataset, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - auto const bad_owning_input_dataset = load_dataset(bad_load_id_state_json); auto const& bad_input_dataset = bad_owning_input_dataset.dataset; @@ -557,22 +437,6 @@ TEST_CASE("API Model") { } SUBCASE("Update error") { - // ID const good_load_id = 2; - // ID const bad_source_update_id = 99; - // load_buffer.set_value(PGM_def_input_sym_load_id, &good_load_id, -1); - // source_update_buffer.set_value(PGM_def_update_source_id, &bad_source_update_id, 0, -1); - - // // TODO(mgovers): remove - // Serializer serializer{input_dataset, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - - // auto const owning_input_dataset = load_dataset(bad_source_update_load_id_state_json); - // auto const& input_dataset = owning_input_dataset.dataset; - - // // TODO(mgovers): remove - // Serializer serializer{single_update_dataset, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - auto const bad_single_owning_update_dataset = load_dataset(bad_source_id_single_update_json); auto const& single_update_dataset = bad_single_owning_update_dataset.dataset; @@ -582,22 +446,6 @@ 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", 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); - // bad_batch_update_dataset.add_buffer("line", 2, 4, nullptr, nullptr); // columnar input for line - // std::vector const bad_batch_line_id{99, 999, 9999, 99999}; - // bad_batch_update_dataset.add_attribute_buffer("line", "id", bad_batch_line_id.data()); - // bad_batch_update_dataset.add_attribute_buffer("line", "from_status", batch_line_from_status.data()); - // bad_batch_update_dataset.add_attribute_buffer("line", "to_status", batch_line_to_status.data()); - - // // TODO(mgovers): remove - // Serializer serializer{bad_batch_update_dataset, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - auto const bad_batch_owning_update_dataset = load_dataset(bad_load_id_batch_update_json); auto const& bad_batch_update_dataset = bad_batch_owning_update_dataset.dataset; @@ -651,14 +499,6 @@ TEST_CASE("API Model") { SUBCASE("Batch calculation error") { SUBCASE("Line bad line id") { - // // wrong id - // load_updates_id[1] = 999; - // load_updates_buffer.set_value(PGM_def_update_sym_load_id, load_updates_id.data(), 1, -1); - - // // TODO(mgovers): remove - // Serializer serializer{batch_update_dataset, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - auto const bad_batch_owning_update_dataset = load_dataset(bad_line_id_batch_update_json); auto const& batch_update_dataset = bad_batch_owning_update_dataset.dataset; @@ -1047,44 +887,7 @@ TEST_CASE("API Model") { } SUBCASE("Forbid link power measurements") { - // // input data - // DatasetConst input_dataset_se{"input", false, 1}; - // auto const construct_model = [&input_dataset_se] { return Model{50.0, input_dataset_se}; }; - - // // node buffer - // std::vector const node_id_se{1, 2}; - // std::vector const node_u_rated_se{10000.0, 10000.0}; - - // // link buffer - // std::vector const link_id_se{3}; - // std::vector const link_from_node_se{1}; - // std::vector const link_to_node_se{2}; - - // // power sensor - // std::vector const power_sensor_id_se{4}; - // std::vector const power_sensor_measured_object_se{3}; - // std::vector const power_sensor_measured_terminal_type_se{0}; - - // input_dataset_se.add_buffer("node", 2, 2, nullptr, nullptr); - // input_dataset_se.add_attribute_buffer("node", "id", node_id_se.data()); - // input_dataset_se.add_attribute_buffer("node", "u_rated", node_u_rated_se.data()); - - // input_dataset_se.add_buffer("link", 1, 1, nullptr, nullptr); - // input_dataset_se.add_attribute_buffer("link", "id", link_id_se.data()); - // input_dataset_se.add_attribute_buffer("link", "from_node", link_from_node_se.data()); - // input_dataset_se.add_attribute_buffer("link", "to_node", link_to_node_se.data()); - SUBCASE("SymPowerSensor") { - // input_dataset_se.add_buffer("sym_power_sensor", 1, 1, nullptr, nullptr); - // input_dataset_se.add_attribute_buffer("sym_power_sensor", "id", power_sensor_id_se.data()); - // input_dataset_se.add_attribute_buffer("sym_power_sensor", "measured_object", - // power_sensor_measured_object_se.data()); - // input_dataset_se.add_attribute_buffer("sym_power_sensor", "measured_terminal_type", - // power_sensor_measured_terminal_type_se.data()); - - // // TODO(mgovers): remove - // Serializer serializer{input_dataset_se, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); auto const input_data_se_json = R"json({ "version": "1.0", "type": "input", @@ -1114,12 +917,6 @@ TEST_CASE("API Model") { } SUBCASE("AsymPowerSensor") { - // input_dataset_se.add_buffer("asym_power_sensor", 2, 2, nullptr, nullptr); - // input_dataset_se.add_attribute_buffer("asym_power_sensor", "id", power_sensor_id_se.data()); - // input_dataset_se.add_attribute_buffer("asym_power_sensor", "measured_object", - // power_sensor_measured_object_se.data()); - // input_dataset_se.add_attribute_buffer("asym_power_sensor", "measured_terminal_type", - // power_sensor_measured_terminal_type_se.data()); auto const input_data_se_json = R"json({ "version": "1.0", "type": "input", @@ -1150,11 +947,6 @@ TEST_CASE("API Model") { } SUBCASE("Test duplicated id") { - // std::vector node_id_2{1, 1, 3}; - // 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()); auto const input_data_2_json = R"json({ "version": "1.0", "type": "input", @@ -1177,27 +969,6 @@ TEST_CASE("API Model") { } SUBCASE("Test non-existing id") { - // 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_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("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()); - - // // TODO(mgovers): remove - // Serializer serializer{input_dataset_2, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); auto const input_data_2_json = R"json({ "version": "1.0", "type": "input", From 0c477720269dacbe40e41c7c914125c034ab2cd1 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Wed, 11 Dec 2024 10:23:32 +0100 Subject: [PATCH 12/44] reorder Signed-off-by: Martijn Govers --- tests/native_api_tests/test_api_model.cpp | 204 +++++++++++----------- 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index 75421bfb3..405ed6ce4 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -118,29 +118,6 @@ auto const state_json = R"json({ } })json"s; -auto const bad_load_id_state_json = R"json({ - "version": "1.0", - "type": "input", - "is_batch": false, - "attributes": {}, - "data": { - "sym_load": [ - {"id": 0, "node": 0, "status": 1, "type": 2, "p_specified": 0, "q_specified": 500} - ], - "source": [ - {"id": 1, "node": 0, "status": 1, "u_ref": 1, "sk": 1000, "rx_ratio": 0} - ], - "node": [ - {"id": 0, "u_rated": 100}, - {"id": 4, "u_rated": 100} - ], - "line": [ - {"id": 5, "from_node": 0, "to_node": 4, "from_status": 0, "to_status": 1}, - {"id": 6, "from_node": 4, "to_node": 0, "from_status": 0, "to_status": 0} - ] - } -})json"s; - auto const single_update_json = R"json({ "version": "1.0", "type": "update", @@ -189,85 +166,6 @@ auto const batch_update_json = R"json({ } ] })json"s; - -auto const bad_source_id_single_update_json = R"json({ - "version": "1.0", - "type": "update", - "is_batch": false, - "attributes": {}, - "data": { - "source": [ - {"id": 99, "u_ref": 0.5} - ], - "sym_load": [ - {"id": 2, "q_specified": 100} - ], - "line": [ - {"id": 5, "from_status": 0, "to_status": 1}, - {"id": 6, "from_status": 0, "to_status": 0} - ] - } -})json"s; - -auto const bad_load_id_batch_update_json = R"json({ - "version": "1.0", - "type": "update", - "is_batch": true, - "attributes": {}, - "data": [ - { - "source": [ - {"id": 1, "u_ref": 0.5} - ], - "sym_load": [ - {"id": 2, "q_specified": 100} - ], - "line": [ - {"id": 99, "from_status": 0, "to_status": 1}, - {"id": 999, "from_status": 0, "to_status": 0} - ] - }, - { - "sym_load": [ - {"id": 2, "q_specified": 300} - ], - "line": [ - {"id": 9999, "from_status": 0, "to_status": 0}, - {"id": 99999, "from_status": 0, "to_status": 0} - ] - } - ] -})json"s; - -auto const bad_line_id_batch_update_json = R"json({ - "version": "1.0", - "type": "update", - "is_batch": true, - "attributes": {}, - "data": [ - { - "source": [ - {"id": 1, "u_ref": 0.5} - ], - "sym_load": [ - {"id": 2, "q_specified": 100} - ], - "line": [ - {"id": 5, "from_status": 0, "to_status": 1}, - {"id": 6, "from_status": 0, "to_status": 0} - ] - }, - { - "sym_load": [ - {"id": 999, "q_specified": 300} - ], - "line": [ - {"id": 5, "from_status": 0, "to_status": 0}, - {"id": 6, "from_status": 0, "to_status": 0} - ] - } - ] -})json"s; } // namespace TEST_CASE("API Model") { @@ -428,6 +326,29 @@ TEST_CASE("API Model") { SUBCASE("Input error handling") { SUBCASE("Construction error") { + auto const bad_load_id_state_json = R"json({ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "sym_load": [ + {"id": 0, "node": 0, "status": 1, "type": 2, "p_specified": 0, "q_specified": 500} + ], + "source": [ + {"id": 1, "node": 0, "status": 1, "u_ref": 1, "sk": 1000, "rx_ratio": 0} + ], + "node": [ + {"id": 0, "u_rated": 100}, + {"id": 4, "u_rated": 100} + ], + "line": [ + {"id": 5, "from_node": 0, "to_node": 4, "from_status": 0, "to_status": 1}, + {"id": 6, "from_node": 4, "to_node": 0, "from_status": 0, "to_status": 0} + ] + } +})json"s; + auto const bad_owning_input_dataset = load_dataset(bad_load_id_state_json); auto const& bad_input_dataset = bad_owning_input_dataset.dataset; @@ -437,6 +358,25 @@ TEST_CASE("API Model") { } SUBCASE("Update error") { + auto const bad_source_id_single_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": false, + "attributes": {}, + "data": { + "source": [ + {"id": 99, "u_ref": 0.5} + ], + "sym_load": [ + {"id": 2, "q_specified": 100} + ], + "line": [ + {"id": 5, "from_status": 0, "to_status": 1}, + {"id": 6, "from_status": 0, "to_status": 0} + ] + } +})json"s; + auto const bad_single_owning_update_dataset = load_dataset(bad_source_id_single_update_json); auto const& single_update_dataset = bad_single_owning_update_dataset.dataset; @@ -446,6 +386,36 @@ TEST_CASE("API Model") { } SUBCASE("Update error in calculation") { + auto const bad_load_id_batch_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "source": [ + {"id": 1, "u_ref": 0.5} + ], + "sym_load": [ + {"id": 2, "q_specified": 100} + ], + "line": [ + {"id": 99, "from_status": 0, "to_status": 1}, + {"id": 999, "from_status": 0, "to_status": 0} + ] + }, + { + "sym_load": [ + {"id": 2, "q_specified": 300} + ], + "line": [ + {"id": 9999, "from_status": 0, "to_status": 0}, + {"id": 99999, "from_status": 0, "to_status": 0} + ] + } + ] +})json"s; + auto const bad_batch_owning_update_dataset = load_dataset(bad_load_id_batch_update_json); auto const& bad_batch_update_dataset = bad_batch_owning_update_dataset.dataset; @@ -499,6 +469,36 @@ TEST_CASE("API Model") { SUBCASE("Batch calculation error") { SUBCASE("Line bad line id") { + auto const bad_line_id_batch_update_json = R"json({ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "source": [ + {"id": 1, "u_ref": 0.5} + ], + "sym_load": [ + {"id": 2, "q_specified": 100} + ], + "line": [ + {"id": 5, "from_status": 0, "to_status": 1}, + {"id": 6, "from_status": 0, "to_status": 0} + ] + }, + { + "sym_load": [ + {"id": 999, "q_specified": 300} + ], + "line": [ + {"id": 5, "from_status": 0, "to_status": 0}, + {"id": 6, "from_status": 0, "to_status": 0} + ] + } + ] +})json"s; + auto const bad_batch_owning_update_dataset = load_dataset(bad_line_id_batch_update_json); auto const& batch_update_dataset = bad_batch_owning_update_dataset.dataset; From 3190f3e2dcea0838fc2cca57614774f17691ce07 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 12 Dec 2024 13:10:41 +0100 Subject: [PATCH 13/44] fix linux build Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/serialization.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp index e8e6ad53b..87fa38f80 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp @@ -104,7 +104,6 @@ inline OwningDatasetConst create_owning_dataset(DatasetWritable& writable_datase for (Idx component_idx{}; component_idx < info.n_components(); ++component_idx) { auto const& component_name = info.component_name(component_idx); auto const& component_meta = MetaData::get_component_by_name(dataset_name, component_name); - Idx const component_elements_per_scenario = info.component_elements_per_scenario(component_idx); Idx const component_size = info.component_total_elements(component_idx); auto& current_indptr = From f210727edb1dee22171665cb08dcfbe85c0e8d46 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 12 Dec 2024 14:27:46 +0100 Subject: [PATCH 14/44] clang-tidy Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/serialization.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp index 87fa38f80..ddf5f1aea 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp @@ -97,8 +97,6 @@ inline OwningDatasetConst create_owning_dataset(DatasetWritable& writable_datase bool const is_batch = info.is_batch(); Idx const batch_size = info.batch_size(); auto const& dataset_name = info.name(); - DatasetMutable dataset_mutable{dataset_name, is_batch, batch_size}; - DatasetConst dataset_const{dataset_mutable}; OwningMemory storage{}; for (Idx component_idx{}; component_idx < info.n_components(); ++component_idx) { From df49310913db37ec68dbb07a8529d6f66ff5001c Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 12 Dec 2024 14:35:28 +0100 Subject: [PATCH 15/44] only mutable owning dataset Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 7 +------ .../power_grid_model_cpp/serialization.hpp | 10 ++++++---- tests/cpp_validation_tests/test_validation.cpp | 18 +++++++++--------- tests/native_api_tests/load_dataset.hpp | 2 +- 4 files changed, 17 insertions(+), 20 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 021c15fbe..e9e41b89f 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 @@ -188,15 +188,10 @@ struct OwningMemory { std::vector> indptrs; }; -struct OwningDatasetMutable { +struct OwningDataset { DatasetMutable dataset; OwningMemory storage{}; }; - -struct OwningDatasetConst { - DatasetConst dataset; - OwningMemory storage{}; -}; } // namespace power_grid_model_cpp #endif // POWER_GRID_MODEL_CPP_DATASET_HPP diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp index ddf5f1aea..d1c3181cb 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/serialization.hpp @@ -92,20 +92,21 @@ class Serializer { detail::UniquePtr serializer_; }; -inline OwningDatasetConst create_owning_dataset(DatasetWritable& writable_dataset) { +inline OwningDataset create_owning_dataset(DatasetWritable& writable_dataset) { auto const& info = writable_dataset.get_info(); bool const is_batch = info.is_batch(); Idx const batch_size = info.batch_size(); auto const& dataset_name = info.name(); + DatasetMutable dataset_mutable{dataset_name, is_batch, batch_size}; OwningMemory storage{}; for (Idx component_idx{}; component_idx < info.n_components(); ++component_idx) { auto const& component_name = info.component_name(component_idx); auto const& component_meta = MetaData::get_component_by_name(dataset_name, component_name); Idx const component_size = info.component_total_elements(component_idx); + Idx const elements_per_scenario = info.component_elements_per_scenario(component_idx); - auto& current_indptr = - storage.indptrs.emplace_back(info.component_elements_per_scenario(component_idx) < 0 ? batch_size + 1 : 0); + auto& current_indptr = storage.indptrs.emplace_back(elements_per_scenario < 0 ? batch_size + 1 : 0); if (!current_indptr.empty()) { current_indptr.at(0) = 0; current_indptr.at(batch_size) = component_size; @@ -113,8 +114,9 @@ inline OwningDatasetConst create_owning_dataset(DatasetWritable& writable_datase Idx* const indptr = current_indptr.empty() ? nullptr : current_indptr.data(); auto& current_buffer = storage.buffers.emplace_back(component_meta, component_size); writable_dataset.set_buffer(component_name, indptr, current_buffer); + dataset_mutable.add_buffer(component_name, elements_per_scenario, component_size, indptr, current_buffer); } - return OwningDatasetConst{.dataset = writable_dataset, .storage = std::move(storage)}; + return OwningDataset{.dataset = std::move(dataset_mutable), .storage = std::move(storage)}; } } // namespace power_grid_model_cpp diff --git a/tests/cpp_validation_tests/test_validation.cpp b/tests/cpp_validation_tests/test_validation.cpp index d93989bfe..84048508c 100644 --- a/tests/cpp_validation_tests/test_validation.cpp +++ b/tests/cpp_validation_tests/test_validation.cpp @@ -60,11 +60,11 @@ auto read_json(std::filesystem::path const& path) { return j; } -OwningDatasetMutable create_result_dataset(OwningDatasetConst const& input, std::string const& dataset_name, - bool is_batch = false, Idx batch_size = 1) { +OwningDataset create_result_dataset(OwningDataset const& input, std::string const& dataset_name, bool is_batch = false, + Idx batch_size = 1) { DatasetInfo const& input_info = input.dataset.get_info(); - OwningDatasetMutable result{.dataset{dataset_name, is_batch, batch_size}, .storage{}}; + OwningDataset result{.dataset{dataset_name, is_batch, batch_size}, .storage{}}; for (Idx component_idx{}; component_idx != input_info.n_components(); ++component_idx) { auto const& component_name = input_info.component_name(component_idx); @@ -82,7 +82,7 @@ OwningDatasetMutable create_result_dataset(OwningDatasetConst const& input, std: return result; } -OwningDatasetConst load_dataset(std::filesystem::path const& path) { +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 @@ -209,7 +209,7 @@ void check_individual_attribute(Buffer const& buffer, Buffer const& ref_buffer, } } -void assert_result(OwningDatasetMutable const& owning_result, OwningDatasetConst const& owning_reference_result, +void assert_result(OwningDataset const& owning_result, OwningDataset const& owning_reference_result, std::map> atol, double rtol) { using namespace std::string_literals; @@ -438,10 +438,10 @@ void add_cases(std::filesystem::path const& case_dir, std::string const& calcula // test case with parameter struct ValidationCase { CaseParam param; - OwningDatasetConst input; - std::optional output; - std::optional update_batch; - std::optional output_batch; + OwningDataset input; + std::optional output; + std::optional update_batch; + std::optional output_batch; }; ValidationCase create_validation_case(CaseParam const& param, std::string const& output_type) { diff --git a/tests/native_api_tests/load_dataset.hpp b/tests/native_api_tests/load_dataset.hpp index e118475a4..16817e967 100644 --- a/tests/native_api_tests/load_dataset.hpp +++ b/tests/native_api_tests/load_dataset.hpp @@ -9,7 +9,7 @@ #include namespace power_grid_model_cpp_test { -inline power_grid_model_cpp::OwningDatasetConst load_dataset(std::string const& json_string) { +inline power_grid_model_cpp::OwningDataset load_dataset(std::string const& json_string) { power_grid_model_cpp::Deserializer deserializer{json_string, PGM_json}; auto& writable_dataset = deserializer.get_dataset(); auto owning_dataset = power_grid_model_cpp::create_owning_dataset(writable_dataset); From a960b1b447c050ec840edd35943a5c6e751f6dd5 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Thu, 12 Dec 2024 15:08:09 +0100 Subject: [PATCH 16/44] use explicit constructor Signed-off-by: Martijn Govers --- tests/cpp_validation_tests/test_validation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cpp_validation_tests/test_validation.cpp b/tests/cpp_validation_tests/test_validation.cpp index 84048508c..883c5a904 100644 --- a/tests/cpp_validation_tests/test_validation.cpp +++ b/tests/cpp_validation_tests/test_validation.cpp @@ -64,7 +64,7 @@ OwningDataset create_result_dataset(OwningDataset const& input, std::string cons Idx batch_size = 1) { DatasetInfo const& input_info = input.dataset.get_info(); - OwningDataset result{.dataset{dataset_name, is_batch, batch_size}, .storage{}}; + OwningDataset result{.dataset = DatasetMutable{dataset_name, is_batch, batch_size}, .storage{}}; for (Idx component_idx{}; component_idx != input_info.n_components(); ++component_idx) { auto const& component_name = input_info.component_name(component_idx); From 3171fa25404c417f9464c586d85123d736c2143e Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Fri, 13 Dec 2024 12:50:00 +0100 Subject: [PATCH 17/44] libboost-headers from pypi Signed-off-by: Martijn Govers --- pyproject.toml | 1 + setup.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d3c9d9813..6c5b98edd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ requires = [ "pybuild-header-dependency", "msgpack-cxx", "nlohmann-json", + "libboost-headers" ] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 82a63d362..742182ec2 100644 --- a/setup.py +++ b/setup.py @@ -30,10 +30,11 @@ def get_required_dependency_include() -> list[str]: either empty list or a list of header path """ try: + import libboost_headers import msgpack_cxx import nlohmann_json - return [str(msgpack_cxx.get_include()), str(nlohmann_json.get_include())] + return [str(msgpack_cxx.get_include()), str(nlohmann_json.get_include()), str(libboost_headers.get_include())] except ImportError: return [] @@ -48,7 +49,7 @@ def get_pre_installed_header_include() -> list[str]: try: from pybuild_header_dependency import HeaderResolver - resolver = HeaderResolver({"eigen": None, "boost": None}) + resolver = HeaderResolver({"eigen": None}) return [str(resolver.get_include())] except ImportError: return [] From f124a51e6c9086f07c00c4d0385a040ada989e4e Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Fri, 13 Dec 2024 13:22:05 +0100 Subject: [PATCH 18/44] add launch file Signed-off-by: Tony Xiang --- .vscode/launch.json | 28 ++++++++++++++++++++++++++++ .vscode/launch.json.license | 3 +++ 2 files changed, 31 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 .vscode/launch.json.license diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..faa4a0191 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug validation test", + "type": "cppdbg", + "request": "launch", + "program": "${command:cmake.buildDirectory}/bin/power_grid_model_validation_tests", + "args": [ + "--test-case=Validation test single", + "--subcase=power_flow/1os2msr-sym-newton_raphson" + ], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "linux": { + "MIMode": "gdb", + }, + "osx": { + "MIMode": "lldb" + }, + } + ] +} \ No newline at end of file diff --git a/.vscode/launch.json.license b/.vscode/launch.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/.vscode/launch.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 From 74061d96c892ae980c068bdcf7b2b6d94703af00 Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Sun, 15 Dec 2024 10:42:51 +0100 Subject: [PATCH 19/44] refactor topology Signed-off-by: Tony Xiang --- .../include/power_grid_model/calculation_parameters.hpp | 1 + .../math_solver/iterative_linear_se_solver.hpp | 2 +- .../math_solver/newton_raphson_se_solver.hpp | 2 +- .../include/power_grid_model/math_solver/observability.hpp | 7 ++++--- .../power_grid_model/include/power_grid_model/topology.hpp | 2 ++ 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/calculation_parameters.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/calculation_parameters.hpp index 399d2d270..53cf13953 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/calculation_parameters.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/calculation_parameters.hpp @@ -141,6 +141,7 @@ using Branch3Idx = std::array; struct MathModelTopology { Idx slack_bus{}; + bool is_radial{}; std::vector phase_shift; std::vector branch_bus_idx; std::vector fill_in; diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_linear_se_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_linear_se_solver.hpp index dbbc32e9e..536456699 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_linear_se_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_linear_se_solver.hpp @@ -95,7 +95,7 @@ template class IterativeLinearSESolver { // preprocess measured value sub_timer = Timer(calculation_info, 2221, "Pre-process measured value"); MeasuredValues const measured_values{y_bus.shared_topology(), input}; - necessary_observability_check(measured_values, y_bus.shared_topology()); + necessary_observability_check(measured_values, y_bus.math_topology()); // prepare matrix, including pre-factorization sub_timer = Timer(calculation_info, 2222, "Prepare matrix, including pre-factorization"); diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_se_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_se_solver.hpp index 7c6ea1978..ecb3ef129 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_se_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_se_solver.hpp @@ -159,7 +159,7 @@ template class NewtonRaphsonSESolver { // preprocess measured value sub_timer = Timer(calculation_info, 2221, "Pre-process measured value"); MeasuredValues const measured_values{y_bus.shared_topology(), input}; - necessary_observability_check(measured_values, y_bus.shared_topology()); + necessary_observability_check(measured_values, y_bus.math_topology()); // initialize voltage with initial angle sub_timer = Timer(calculation_info, 2223, "Initialize voltages"); diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp index e0f76d015..04475ed2f 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp @@ -5,6 +5,7 @@ #pragma once #include "measured_values.hpp" +#include "y_bus.hpp" #include "../common/exception.hpp" @@ -61,10 +62,10 @@ std::tuple count_voltage_sensors(const Idx n_bus, const MeasuredValues } // namespace detail template inline void necessary_observability_check(MeasuredValues const& measured_values, - std::shared_ptr const& topo) { + MathModelTopology const& topo) { - Idx const n_bus{topo->n_bus()}; - std::vector const& branch_bus_idx{topo->branch_bus_idx}; + Idx const n_bus{topo.n_bus()}; + std::vector const& branch_bus_idx{topo.branch_bus_idx}; auto const [n_voltage_sensor, n_voltage_phasor_sensor] = detail::count_voltage_sensors(n_bus, measured_values); if (n_voltage_sensor < 1) { diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/topology.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/topology.hpp index 45ce72223..5968ebfcb 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/topology.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/topology.hpp @@ -241,10 +241,12 @@ class Topology { // no cycle, the graph is pure tree structure // just reverse the node std::reverse(dfs_node.begin(), dfs_node.end()); + math_topo_single.is_radial = true; } else { // with cycles, meshed graph // use minimum degree math_topo_single.fill_in = reorder_node(dfs_node, back_edges); + math_topo_single.is_radial = false; } // initialize phase shift math_topo_single.phase_shift.resize(dfs_node.size()); From 257321e433193f24540da51af88c110f3a5d904d Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Sun, 15 Dec 2024 10:48:38 +0100 Subject: [PATCH 20/44] pass ybus structure into observability Signed-off-by: Tony Xiang --- .../math_solver/iterative_linear_se_solver.hpp | 2 +- .../math_solver/newton_raphson_se_solver.hpp | 2 +- .../include/power_grid_model/math_solver/observability.hpp | 6 +++--- .../include/power_grid_model/math_solver/y_bus.hpp | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_linear_se_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_linear_se_solver.hpp index 536456699..0eca1af4f 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_linear_se_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_linear_se_solver.hpp @@ -95,7 +95,7 @@ template class IterativeLinearSESolver { // preprocess measured value sub_timer = Timer(calculation_info, 2221, "Pre-process measured value"); MeasuredValues const measured_values{y_bus.shared_topology(), input}; - necessary_observability_check(measured_values, y_bus.math_topology()); + necessary_observability_check(measured_values, y_bus.math_topology(), y_bus.y_bus_structure()); // prepare matrix, including pre-factorization sub_timer = Timer(calculation_info, 2222, "Prepare matrix, including pre-factorization"); diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_se_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_se_solver.hpp index ecb3ef129..7e3e451f6 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_se_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_se_solver.hpp @@ -159,7 +159,7 @@ template class NewtonRaphsonSESolver { // preprocess measured value sub_timer = Timer(calculation_info, 2221, "Pre-process measured value"); MeasuredValues const measured_values{y_bus.shared_topology(), input}; - necessary_observability_check(measured_values, y_bus.math_topology()); + necessary_observability_check(measured_values, y_bus.math_topology(), y_bus.y_bus_structure()); // initialize voltage with initial angle sub_timer = Timer(calculation_info, 2223, "Initialize voltages"); diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp index 04475ed2f..c478dcb05 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp @@ -61,9 +61,9 @@ std::tuple count_voltage_sensors(const Idx n_bus, const MeasuredValues } // namespace detail template -inline void necessary_observability_check(MeasuredValues const& measured_values, - MathModelTopology const& topo) { - +inline void necessary_observability_check(MeasuredValues const& measured_values, MathModelTopology const& topo, + YBusStructure const& y_bus_structure) { + (void)y_bus_structure; // TODO remove when used Idx const n_bus{topo.n_bus()}; std::vector const& branch_bus_idx{topo.branch_bus_idx}; diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp index ee7fb2d3c..c465bae4e 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp @@ -294,6 +294,7 @@ template class YBus { } // getter + YBusStructure const& y_bus_structure() const { return *y_bus_struct_; } Idx size() const { return static_cast(bus_entry().size()); } Idx nnz() const { return row_indptr().back(); } Idx nnz_lu() const { return row_indptr_lu().back(); } From ac6318e697b0ec3cfba22231a979ffd23f776921 Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Sun, 15 Dec 2024 11:12:34 +0100 Subject: [PATCH 21/44] refactor sensor flow count Signed-off-by: Tony Xiang --- .../math_solver/observability.hpp | 81 ++++++++++--------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp index c478dcb05..7fb2cbfb7 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp @@ -13,37 +13,6 @@ namespace power_grid_model::math_solver { namespace detail { -template -Idx count_branch_sensors(const std::vector& branch_bus_idx, const Idx n_bus, - const MeasuredValues& measured_values) { - Idx n_branch_sensor{}; - std::vector measured_nodes(n_bus, false); - for (Idx branch = 0; branch != static_cast(branch_bus_idx.size()); ++branch) { - auto const& [node_from, node_to] = branch_bus_idx[branch]; - if (node_from == -1 || node_to == -1) { - continue; - } - if ((measured_values.has_branch_from(branch) || measured_values.has_branch_to(branch)) && - !(measured_nodes[node_from] && measured_nodes[node_to])) { - n_branch_sensor++; - measured_nodes[node_from] = true; - measured_nodes[node_to] = true; - } - } - return n_branch_sensor; -} - -template -Idx count_bus_injection_sensors(const Idx n_bus, const MeasuredValues& measured_values) { - Idx n_injection_sensor{}; - for (Idx bus = 0; bus != n_bus; ++bus) { - if (measured_values.has_bus_injection(bus)) { - n_injection_sensor++; - } - } - return n_injection_sensor; -} - template std::tuple count_voltage_sensors(const Idx n_bus, const MeasuredValues& measured_values) { Idx n_voltage_sensor{}; @@ -59,27 +28,63 @@ std::tuple count_voltage_sensors(const Idx n_bus, const MeasuredValues return std::make_tuple(n_voltage_sensor, n_voltage_phasor_sensor); } +// count flow sensors into ybus structure like +// lower triangle part is always zero +// for diagonal part, it will be one if there is bus injection +// for upper triangle part, it will be one if there is branch flow sensor and the branch is fully connected +template +std::vector count_flow_sensors(MeasuredValues const& measured_values, MathModelTopology const& topo, + YBusStructure const& y_bus_structure) { + Idx const n_bus{topo.n_bus()}; + std::vector flow_sensors(y_bus_structure.row_indptr.back(), 0); // initialize all to zero + for (Idx row = 0; row != n_bus; ++row) { + // lower triangle is ignored and kept as zero + // diagonal for bus injection measurement + if (measured_values.has_bus_injection(row)) { + flow_sensors[y_bus_structure.bus_entry[row]] = 1; + } + // upper triangle for branch flow measurement + for (Idx ybus_index = y_bus_structure.bus_entry[row] + 1; ybus_index != y_bus_structure.row_indptr[row + 1]; + ++ybus_index) { + for (Idx element_index = y_bus_structure.y_bus_entry_indptr[ybus_index]; + element_index != y_bus_structure.y_bus_entry_indptr[ybus_index + 1]; ++element_index) { + YBusElement const& element = y_bus_structure.y_bus_element[element_index]; + // shunt should not be considered + // if the branch is fully connected and measured, we consider it as a valid flow sensor + // we only need one flow sensor, so the loop will break + if (element.element_type != YBusElementType::shunt) { + Idx const branch = element.idx; + if ((measured_values.has_branch_from(branch) || measured_values.has_branch_to(branch)) && + topo.branch_bus_idx[branch][0] != -1 && topo.branch_bus_idx[branch][1] != -1) { + flow_sensors[ybus_index] = 1; + break; + } + } + } + } + } + return flow_sensors; +} + } // namespace detail template inline void necessary_observability_check(MeasuredValues const& measured_values, MathModelTopology const& topo, YBusStructure const& y_bus_structure) { - (void)y_bus_structure; // TODO remove when used Idx const n_bus{topo.n_bus()}; - std::vector const& branch_bus_idx{topo.branch_bus_idx}; auto const [n_voltage_sensor, n_voltage_phasor_sensor] = detail::count_voltage_sensors(n_bus, measured_values); if (n_voltage_sensor < 1) { throw NotObservableError{"no voltage sensor found"}; } - Idx const n_injection_sensor = detail::count_bus_injection_sensors(n_bus, measured_values); - Idx const n_branch_sensor = detail::count_branch_sensors(branch_bus_idx, n_bus, measured_values); - Idx const n_power_sensor = n_injection_sensor + n_branch_sensor; + auto const flow_sensors = detail::count_flow_sensors(measured_values, topo, y_bus_structure); + // count flow sensors, note we manually specify the intial value type to avoid overflow + Idx const n_flow_sensor = std::reduce(flow_sensors.cbegin(), flow_sensors.cend(), Idx{}, std::plus{}); - if (n_voltage_phasor_sensor == 0 && n_power_sensor < n_bus - 1) { + if (n_voltage_phasor_sensor == 0 && n_flow_sensor < n_bus - 1) { throw NotObservableError{}; } - if (n_voltage_phasor_sensor > 0 && n_power_sensor + n_voltage_phasor_sensor < n_bus) { + if (n_voltage_phasor_sensor > 0 && n_flow_sensor + n_voltage_phasor_sensor < n_bus) { throw NotObservableError{}; } } From 10bd054a2fb4fb1b10e7e7b395b33a9342625e3e Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Sun, 15 Dec 2024 11:17:29 +0100 Subject: [PATCH 22/44] add test case of not independent sensors Signed-off-by: Tony Xiang --- .../not-independent-sensor/input.json | 31 +++++++++++++++++++ .../not-independent-sensor/input.json.license | 3 ++ .../not-independent-sensor/params.json | 8 +++++ .../params.json.license | 3 ++ .../not-independent-sensor/sym_output.json | 8 +++++ .../sym_output.json.license | 3 ++ 6 files changed, 56 insertions(+) create mode 100644 tests/data/state_estimation/not-independent-sensor/input.json create mode 100644 tests/data/state_estimation/not-independent-sensor/input.json.license create mode 100644 tests/data/state_estimation/not-independent-sensor/params.json create mode 100644 tests/data/state_estimation/not-independent-sensor/params.json.license create mode 100644 tests/data/state_estimation/not-independent-sensor/sym_output.json create mode 100644 tests/data/state_estimation/not-independent-sensor/sym_output.json.license diff --git a/tests/data/state_estimation/not-independent-sensor/input.json b/tests/data/state_estimation/not-independent-sensor/input.json new file mode 100644 index 000000000..308bcddf3 --- /dev/null +++ b/tests/data/state_estimation/not-independent-sensor/input.json @@ -0,0 +1,31 @@ +{ + "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}, + {"id": 10, "node": 7, "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": 0, "q_measured": 0}, + {"id": 9, "measured_object": 2, "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} + ] + } +} \ No newline at end of file diff --git a/tests/data/state_estimation/not-independent-sensor/input.json.license b/tests/data/state_estimation/not-independent-sensor/input.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/state_estimation/not-independent-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/not-independent-sensor/params.json b/tests/data/state_estimation/not-independent-sensor/params.json new file mode 100644 index 000000000..a0e22b030 --- /dev/null +++ b/tests/data/state_estimation/not-independent-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/not-independent-sensor/params.json.license b/tests/data/state_estimation/not-independent-sensor/params.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/state_estimation/not-independent-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/not-independent-sensor/sym_output.json b/tests/data/state_estimation/not-independent-sensor/sym_output.json new file mode 100644 index 000000000..83bbecf19 --- /dev/null +++ b/tests/data/state_estimation/not-independent-sensor/sym_output.json @@ -0,0 +1,8 @@ +{ + "version": "1.0", + "type": "sym_output", + "is_batch": false, + "attributes": {}, + "data": { + } +} \ No newline at end of file diff --git a/tests/data/state_estimation/not-independent-sensor/sym_output.json.license b/tests/data/state_estimation/not-independent-sensor/sym_output.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/state_estimation/not-independent-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 5535caa2c3a26c024b9910aa37db3b84bf4e2809 Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Sun, 15 Dec 2024 11:38:42 +0100 Subject: [PATCH 23/44] add full observability check Signed-off-by: Tony Xiang --- .../math_solver/observability.hpp | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp index 7fb2cbfb7..8361ff050 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp @@ -66,6 +66,42 @@ std::vector count_flow_sensors(MeasuredValues const& measured_value return flow_sensors; } +// re-organize the flow sensor for radial grid without phasor measurement +// this mutate the flow sensor vector to try to assign injection sensor to branch sensor +// all the branch should be measured if the system is observable +// this is a sufficient condition check +void assign_injection_sensor(YBusStructure const& y_bus_structure, std::vector& flow_sensors) { + Idx const n_bus = std::ssize(y_bus_structure.row_indptr) - 1; + // loop the row without the last bus + for (Idx row = 0; row != n_bus - 1; ++row) { + Idx const current_bus = row; + Idx const bus_entry_current = y_bus_structure.bus_entry[current_bus]; + Idx const branch_entry_upstream = bus_entry_current + 1; + // there should be only one upstream branch in the upper diagonal + // so the next of branch_entry_upstream is already the next row + // because the grid is radial + assert(y_bus_structure.row_indptr[current_bus + 1] == branch_entry_upstream + 1); + Idx const upstream_bus = y_bus_structure.col_indices[branch_entry_upstream]; + Idx const bus_entry_upstream = y_bus_structure.bus_entry[upstream_bus]; + // if the upstream branch is not measured + if (flow_sensors[branch_entry_upstream] == 0) { + if (flow_sensors[bus_entry_current] == 1) { + // try to steal from current bus + flow_sensors[bus_entry_current] = 0; + flow_sensors[branch_entry_upstream] = 1; + } else if (flow_sensors[bus_entry_upstream] == 1) { + // if not possible, steal from upstream bus + flow_sensors[bus_entry_upstream] = 0; + flow_sensors[branch_entry_upstream] = 1; + } + } + // remove the current bus injection regardless of the original state + flow_sensors[bus_entry_current] = 0; + } + // set last bus injection to zero + flow_sensors[y_bus_structure.bus_entry[n_bus - 1]] = 0; +} + } // namespace detail template inline void necessary_observability_check(MeasuredValues const& measured_values, MathModelTopology const& topo, @@ -74,19 +110,32 @@ inline void necessary_observability_check(MeasuredValues const& measured_va auto const [n_voltage_sensor, n_voltage_phasor_sensor] = detail::count_voltage_sensors(n_bus, measured_values); if (n_voltage_sensor < 1) { - throw NotObservableError{"no voltage sensor found"}; + throw NotObservableError{"No voltage sensor found!\n"}; } - auto const flow_sensors = detail::count_flow_sensors(measured_values, topo, y_bus_structure); + std::vector flow_sensors = detail::count_flow_sensors(measured_values, topo, y_bus_structure); // count flow sensors, note we manually specify the intial value type to avoid overflow Idx const n_flow_sensor = std::reduce(flow_sensors.cbegin(), flow_sensors.cend(), Idx{}, std::plus{}); + // check nessessary condition for observability if (n_voltage_phasor_sensor == 0 && n_flow_sensor < n_bus - 1) { throw NotObservableError{}; } if (n_voltage_phasor_sensor > 0 && n_flow_sensor + n_voltage_phasor_sensor < n_bus) { throw NotObservableError{}; } + + // for radial grid without phasor measurement, try to assign injection sensor to branch sensor + // we can then check sufficient condition for observability + if (topo.is_radial && n_voltage_phasor_sensor == 0) { + detail::assign_injection_sensor(y_bus_structure, flow_sensors); + // count flow sensors again + Idx const n_flow_sensor_new = std::reduce(flow_sensors.cbegin(), flow_sensors.cend(), Idx{}, std::plus{}); + if (n_flow_sensor_new < n_bus - 1) { + throw NotObservableError{"The number of power sensors seems to be sufficient, but they are not independent " + "enough. The system is still not observable.\n"}; + } + } } } // namespace power_grid_model::math_solver From 981d866124b8263b9449787a904aa84ee3621025 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 16 Dec 2024 07:55:48 +0100 Subject: [PATCH 24/44] resolve comment Signed-off-by: Martijn Govers --- tests/native_api_tests/test_api_model.cpp | 4 ++-- .../native_api_tests/test_api_model_update.cpp | 18 +++++------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index 405ed6ce4..10738634b 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -95,7 +95,7 @@ void check_throws_with(Func&& func, PGM_ErrorCode const& reference_error, std::s } } -auto const state_json = R"json({ +auto const complete_state_json = R"json({ "version": "1.0", "type": "input", "is_batch": false, @@ -173,7 +173,7 @@ TEST_CASE("API Model") { Options options{}; - auto const owning_input_dataset = load_dataset(state_json); + auto const owning_input_dataset = load_dataset(complete_state_json); auto const& input_dataset = owning_input_dataset.dataset; auto const single_owning_update_dataset = load_dataset(single_update_json); diff --git a/tests/native_api_tests/test_api_model_update.cpp b/tests/native_api_tests/test_api_model_update.cpp index 0835502f3..8dd14f324 100644 --- a/tests/native_api_tests/test_api_model_update.cpp +++ b/tests/native_api_tests/test_api_model_update.cpp @@ -297,7 +297,7 @@ constexpr double i_shunt = 0.015 / 0.025 * i; constexpr double i_load = 0.005 / 0.025 * i; } // namespace test -auto const state_json = R"json({ +auto const complete_state_json = R"json({ "version": "1.0", "type": "input", "is_batch": false, @@ -378,20 +378,12 @@ auto const update_vector_json = R"json({ } // namespace TEST_CASE("API model - all updates") { - // TODO(mgovers): remove - // Serializer serializer{state.get_input_dataset(), PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - - auto const owning_input_dataset = load_dataset(state_json); + auto const owning_input_dataset = load_dataset(complete_state_json); auto const& input_dataset = owning_input_dataset.dataset; auto const& input_info = input_dataset.get_info(); auto model = Model{50.0, input_dataset}; - // TODO(mgovers): remove - // Serializer serializer{update_data, PGM_json}; - // auto const str = serializer.get_to_zero_terminated_string(0, 2); - auto const owning_update_dataset = load_dataset(update_json); auto const& update_data = owning_update_dataset.dataset; @@ -443,7 +435,7 @@ TEST_CASE("API model - all updates") { } TEST_CASE("API model - updates w/ alternating compute mode") { - auto const owning_input_dataset = load_dataset(state_json); + auto const owning_input_dataset = load_dataset(complete_state_json); auto const& input_dataset = owning_input_dataset.dataset; auto model = Model{50.0, input_dataset}; @@ -626,7 +618,7 @@ auto const complete_update_json = R"json({ } // namespace TEST_CASE("API model - incomplete input") { - auto const complete_owning_input_dataset = load_dataset(state_json); + auto const complete_owning_input_dataset = load_dataset(complete_state_json); auto const& complete_input_data = complete_owning_input_dataset.dataset; auto const incomplete_owning_input_dataset = load_dataset(incomplete_state_json); @@ -793,7 +785,7 @@ auto const second_scenario_update_json = R"json({ })json"s; TEST_CASE("API model - Incomplete scenario update followed by complete") { - auto const complete_owning_input_dataset = load_dataset(state_json); + auto const complete_owning_input_dataset = load_dataset(complete_state_json); auto const incomplete_owning_input_dataset = load_dataset(incomplete_state_json); auto const& complete_input_data = complete_owning_input_dataset.dataset; From 55b779c19223f07f6e8b7abadc95ff9b7ab62011 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 16 Dec 2024 08:28:01 +0100 Subject: [PATCH 25/44] fix sonar cloud Signed-off-by: Martijn Govers --- tests/cpp_validation_tests/test_validation.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/cpp_validation_tests/test_validation.cpp b/tests/cpp_validation_tests/test_validation.cpp index 883c5a904..a52a05c7f 100644 --- a/tests/cpp_validation_tests/test_validation.cpp +++ b/tests/cpp_validation_tests/test_validation.cpp @@ -35,15 +35,6 @@ class UnsupportedValidationCase : public PowerGridError { }()} {} }; -class OptionalNotInitialized : public PowerGridError { - public: - OptionalNotInitialized(std::string const& object) - : PowerGridError{[&]() { - using namespace std::string_literals; - return "Optional "s + object + " object not initialized"s; - }()} {} -}; - using nlohmann::json; auto read_file(std::filesystem::path const& path) { From b918f381706c726d24ae4641b6ba3c6ce102943e Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 16 Dec 2024 08:54:14 +0100 Subject: [PATCH 26/44] more sonar cloud Signed-off-by: Martijn Govers --- tests/native_api_tests/test_api_model.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index 10738634b..0709749fd 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -378,9 +378,9 @@ TEST_CASE("API Model") { })json"s; auto const bad_single_owning_update_dataset = load_dataset(bad_source_id_single_update_json); - auto const& single_update_dataset = bad_single_owning_update_dataset.dataset; - - auto const bad_update_lambda = [&model, &single_update_dataset]() { model.update(single_update_dataset); }; + auto const bad_update_lambda = [&model, &bad_single_owning_update_dataset]() { + model.update(bad_single_owning_update_dataset.dataset); + }; check_throws_with(bad_update_lambda, PGM_regular_error, "The id cannot be found:"s); } @@ -417,11 +417,9 @@ TEST_CASE("API Model") { })json"s; auto const bad_batch_owning_update_dataset = load_dataset(bad_load_id_batch_update_json); - auto const& bad_batch_update_dataset = bad_batch_owning_update_dataset.dataset; - auto const bad_calc_with_update_lambda = [&model, &options, &batch_output_dataset, - &bad_batch_update_dataset]() { - model.calculate(options, batch_output_dataset, bad_batch_update_dataset); + &bad_batch_owning_update_dataset]() { + model.calculate(options, batch_output_dataset, bad_batch_owning_update_dataset.dataset); }; check_throws_with(bad_calc_with_update_lambda, PGM_batch_error, "The id cannot be found:"s); } @@ -500,11 +498,10 @@ TEST_CASE("API Model") { })json"s; auto const bad_batch_owning_update_dataset = load_dataset(bad_line_id_batch_update_json); - auto const& batch_update_dataset = bad_batch_owning_update_dataset.dataset; // failed in batch scenario 1 try { - model.calculate(options, batch_output_dataset, batch_update_dataset); + model.calculate(options, batch_output_dataset, bad_batch_owning_update_dataset.dataset); FAIL("Expected batch calculation error not thrown."); } catch (PowerGridBatchError const& e) { CHECK(e.error_code() == PGM_batch_error); From 16ea1e30e53a692dfb58150f03f7d3d42115edd7 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 16 Dec 2024 09:09:32 +0100 Subject: [PATCH 27/44] minor Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/handle.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/handle.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/handle.hpp index d08b99e11..47108cc59 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/handle.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/handle.hpp @@ -40,8 +40,8 @@ class PowerGridBatchError : public PowerGridError { std::string error_message; }; - PowerGridBatchError(std::string const& message, std::vector failed_scenarios_c) - : PowerGridError{message}, failed_scenarios_{std::move(failed_scenarios_c)} {} + PowerGridBatchError(std::string message, std::vector failed_scenarios_c) + : PowerGridError{std::move(message)}, failed_scenarios_{std::move(failed_scenarios_c)} {} Idx error_code() const noexcept override { return PGM_batch_error; } std::vector const& failed_scenarios() const { return failed_scenarios_; } From 1d6d13308fdd2fa7731fc04bf8d6cf69622ec12e Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 16 Dec 2024 10:13:41 +0100 Subject: [PATCH 28/44] throw when component type not found in dataset Signed-off-by: Martijn Govers --- .../include/power_grid_model_cpp/dataset.hpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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 e9e41b89f..eaf12f11f 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 @@ -13,6 +13,15 @@ #include "power_grid_model_c/dataset.h" namespace power_grid_model_cpp { +class ComponentTypeNotFound : public PowerGridError { + public: + ComponentTypeNotFound(std::string const& component) + : PowerGridError{[&]() { + using namespace std::string_literals; + return "ComponentType"s + component + " not found"s; + }()} {} + ComponentTypeNotFound(std::string_view component) : ComponentTypeNotFound{std::string{component}} {} +}; class DatasetInfo { @@ -51,7 +60,7 @@ class DatasetInfo { return idx; } } - return -1; + throw ComponentTypeNotFound{component}; } private: From 336e1afe9d8440cebff51053815de7004376f58f Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 16 Dec 2024 14:21:50 +0100 Subject: [PATCH 29/44] remove unnecessary header guards protected by pragma once Signed-off-by: Martijn Govers --- .pre-commit-config.yaml | 2 +- tests/cpp_unit_tests/test_math_solver_common.hpp | 4 ---- tests/cpp_unit_tests/test_math_solver_pf.hpp | 4 ---- tests/cpp_unit_tests/test_math_solver_se.hpp | 4 ---- tests/cpp_unit_tests/test_optimizer.hpp | 4 ---- tests/native_api_tests/load_dataset.hpp | 4 ---- 6 files changed, 1 insertion(+), 21 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7bce9224f..d07c0dd5e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: hooks: - id: black-jupyter - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.2 + rev: v1.13.0 hooks: - id: mypy additional_dependencies: diff --git a/tests/cpp_unit_tests/test_math_solver_common.hpp b/tests/cpp_unit_tests/test_math_solver_common.hpp index 37f027b31..da4f005d8 100644 --- a/tests/cpp_unit_tests/test_math_solver_common.hpp +++ b/tests/cpp_unit_tests/test_math_solver_common.hpp @@ -5,8 +5,6 @@ // In this unit test the powerflow solvers are tested #pragma once -#ifndef POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_COMMON_HPP -#define POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_COMMON_HPP #include #include @@ -269,5 +267,3 @@ template struct SteadyStateSolverTestGrid { }; } // namespace power_grid_model - -#endif // POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_COMMON_HPP diff --git a/tests/cpp_unit_tests/test_math_solver_pf.hpp b/tests/cpp_unit_tests/test_math_solver_pf.hpp index ffe927514..1971d83ea 100644 --- a/tests/cpp_unit_tests/test_math_solver_pf.hpp +++ b/tests/cpp_unit_tests/test_math_solver_pf.hpp @@ -5,8 +5,6 @@ // In this unit test the powerflow solvers are tested #pragma once -#ifndef POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_PF_HPP -#define POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_PF_HPP #include "test_math_solver_common.hpp" @@ -146,5 +144,3 @@ TEST_CASE_TEMPLATE_DEFINE("Test math solver - PF", SolverType, test_math_solver_ } } // namespace power_grid_model - -#endif // POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_PF_HPP diff --git a/tests/cpp_unit_tests/test_math_solver_se.hpp b/tests/cpp_unit_tests/test_math_solver_se.hpp index dfbbceb5a..1d4268261 100644 --- a/tests/cpp_unit_tests/test_math_solver_se.hpp +++ b/tests/cpp_unit_tests/test_math_solver_se.hpp @@ -5,8 +5,6 @@ // In this unit test the powerflow solvers are tested #pragma once -#ifndef POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_SE_HPP -#define POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_SE_HPP #include "test_math_solver_common.hpp" @@ -520,5 +518,3 @@ TEST_CASE_TEMPLATE_DEFINE("Test math solver - SE, measurements", SolverType, tes } } // namespace power_grid_model - -#endif // POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_MATH_SOLVER_SE_HPP diff --git a/tests/cpp_unit_tests/test_optimizer.hpp b/tests/cpp_unit_tests/test_optimizer.hpp index c941ef8d3..7f24b915d 100644 --- a/tests/cpp_unit_tests/test_optimizer.hpp +++ b/tests/cpp_unit_tests/test_optimizer.hpp @@ -3,8 +3,6 @@ // SPDX-License-Identifier: MPL-2.0 #pragma once -#ifndef POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_OPTIMIZER_HPP -#define POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_OPTIMIZER_HPP #include #include @@ -250,5 +248,3 @@ template <> struct get_attributes_list { }; } // namespace meta_data } // namespace power_grid_model - -#endif // POWER_GRID_MODEL_TESTS_CPP_UNIT_TESTS_TEST_OPTIMIZER_HPP diff --git a/tests/native_api_tests/load_dataset.hpp b/tests/native_api_tests/load_dataset.hpp index 16817e967..4354ab7a1 100644 --- a/tests/native_api_tests/load_dataset.hpp +++ b/tests/native_api_tests/load_dataset.hpp @@ -3,8 +3,6 @@ // SPDX-License-Identifier: MPL-2.0 #pragma once -#ifndef POWER_GRID_MODEL_TESTS_NATIVE_API_TESTS_LOAD_DATASET_HPP -#define POWER_GRID_MODEL_TESTS_NATIVE_API_TESTS_LOAD_DATASET_HPP #include @@ -17,5 +15,3 @@ inline power_grid_model_cpp::OwningDataset load_dataset(std::string const& json_ return owning_dataset; } } // namespace power_grid_model_cpp_test - -#endif // POWER_GRID_MODEL_TESTS_NATIVE_API_TESTS_LOAD_DATASET_HPP From 0f113b477437da532e633e657e0c0196ddc09e11 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:18:04 +0100 Subject: [PATCH 30/44] add inline Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model/math_solver/observability.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp index 8361ff050..cac259601 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp @@ -70,7 +70,7 @@ std::vector count_flow_sensors(MeasuredValues const& measured_value // this mutate the flow sensor vector to try to assign injection sensor to branch sensor // all the branch should be measured if the system is observable // this is a sufficient condition check -void assign_injection_sensor(YBusStructure const& y_bus_structure, std::vector& flow_sensors) { +inline void assign_injection_sensor(YBusStructure const& y_bus_structure, std::vector& flow_sensors) { Idx const n_bus = std::ssize(y_bus_structure.row_indptr) - 1; // loop the row without the last bus for (Idx row = 0; row != n_bus - 1; ++row) { From 7fc88db07f94d51e0fb36dfc0a6a0c7b67c2920f Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Tue, 17 Dec 2024 12:26:16 +0100 Subject: [PATCH 31/44] fix current unit test Signed-off-by: Tony Xiang --- tests/cpp_unit_tests/test_observability.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/cpp_unit_tests/test_observability.cpp b/tests/cpp_unit_tests/test_observability.cpp index 21006a09e..7411350e6 100644 --- a/tests/cpp_unit_tests/test_observability.cpp +++ b/tests/cpp_unit_tests/test_observability.cpp @@ -18,8 +18,9 @@ void check_not_observable(MathModelTopology const& topo, MathModelParam const y_bus{topo_ptr, param_ptr}; math_solver::MeasuredValues const measured_values{y_bus.shared_topology(), se_input}; - CHECK_THROWS_AS(math_solver::necessary_observability_check(measured_values, y_bus.shared_topology()), - NotObservableError); + CHECK_THROWS_AS( + math_solver::necessary_observability_check(measured_values, y_bus.math_topology(), y_bus.y_bus_structure()), + NotObservableError); } } // namespace @@ -61,7 +62,8 @@ TEST_CASE("Necessary observability check") { auto topo_ptr = std::make_shared(topo); YBus const y_bus{topo_ptr, param_ptr}; math_solver::MeasuredValues const measured_values{y_bus.shared_topology(), se_input}; - CHECK_NOTHROW(math_solver::necessary_observability_check(measured_values, y_bus.shared_topology())); + CHECK_NOTHROW(math_solver::necessary_observability_check(measured_values, y_bus.math_topology(), + y_bus.y_bus_structure())); } SUBCASE("No voltage sensor") { From ece3b41377ea0edef56f2ce4579311d0359ad6f1 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 17 Dec 2024 14:58:07 +0100 Subject: [PATCH 32/44] also add launch configuration for windows Signed-off-by: Martijn Govers --- .vscode/launch.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index faa4a0191..9896cd1ef 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ "version": "0.2.0", "configurations": [ { - "name": "Debug validation test", + "name": "Debug validation test [Unix]", "type": "cppdbg", "request": "launch", "program": "${command:cmake.buildDirectory}/bin/power_grid_model_validation_tests", @@ -23,6 +23,20 @@ "osx": { "MIMode": "lldb" }, + }, + { + "name": "Debug validation test [Windows]", + "type": "cppvsdbg", + "request": "launch", + "program": "${command:cmake.buildDirectory}/bin/power_grid_model_validation_tests.exe", + "args": [ + "--test-case=Validation test single", + "--subcase=power_flow/1os2msr-sym-newton_raphson" + ], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, } ] } \ No newline at end of file From 569d8aa8f5b4e2214e2ae88696164b4e99f8fcab Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 17 Dec 2024 15:01:08 +0100 Subject: [PATCH 33/44] fix deprecated Signed-off-by: Martijn Govers --- .vscode/launch.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 9896cd1ef..f13975366 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -36,7 +36,7 @@ "stopAtEntry": false, "cwd": "${fileDirname}", "environment": [], - "externalConsole": false, + "console": "integratedTerminal", } ] } \ No newline at end of file From ef3937785b6604603a29e5f7283d857c81a0f2af Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Tue, 17 Dec 2024 15:53:48 +0100 Subject: [PATCH 34/44] add new test with not-independent power sensor Signed-off-by: Tony Xiang --- tests/cpp_unit_tests/test_observability.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/cpp_unit_tests/test_observability.cpp b/tests/cpp_unit_tests/test_observability.cpp index 7411350e6..e7dc81740 100644 --- a/tests/cpp_unit_tests/test_observability.cpp +++ b/tests/cpp_unit_tests/test_observability.cpp @@ -26,12 +26,13 @@ void check_not_observable(MathModelTopology const& topo, MathModelParam bus_2 + // it is not independent with injection sensor of bus_2 + topo.power_sensors_per_branch_from = {from_sparse, {0, 0, 1, 1}}; + // set non phasor measurement + se_input.measured_voltage = {{{1.0, nan}, 1.0}}; + // this will throw NotObservableError + check_not_observable(topo, param, se_input); + } } } // namespace power_grid_model From d0bf3b90c9bba4bb0333ed9d51cf7cacaa0eae2e Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Tue, 17 Dec 2024 16:02:45 +0100 Subject: [PATCH 35/44] xfail validation case Signed-off-by: Tony Xiang --- .../data/state_estimation/not-independent-sensor/params.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/data/state_estimation/not-independent-sensor/params.json b/tests/data/state_estimation/not-independent-sensor/params.json index a0e22b030..63b4f2c44 100644 --- a/tests/data/state_estimation/not-independent-sensor/params.json +++ b/tests/data/state_estimation/not-independent-sensor/params.json @@ -4,5 +4,9 @@ "atol": { "default": 1e-8, ".+_residual": 5e-4 + }, + "fail": { + "raises": "NotObservableError", + "reason": "Power measurements are not independent" } } \ No newline at end of file From 43dc814b19bc9510999c4d3ec384111a8306e501 Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Wed, 18 Dec 2024 14:29:42 +0100 Subject: [PATCH 36/44] documentation Signed-off-by: Tony Xiang --- docs/user_manual/calculations.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/user_manual/calculations.md b/docs/user_manual/calculations.md index 958607396..0501a9041 100644 --- a/docs/user_manual/calculations.md +++ b/docs/user_manual/calculations.md @@ -57,10 +57,10 @@ Output: - Power flow through branches - Deviation between measurement values and estimated state -In order to perform a state estimation, the system should be observable. If the system is not observable, the calculation will -raise a sparse matrix error; the matrix reprensentation of the system is too sparse to solve and particular so when it is -singular. In short, meeting the requirement of observability indicates that the system is either an overdetermined system (when the number of measurements is larger than the number of -unknowns) or a balanced system (when the number of measurements is equal to the number of unknowns). For each node, there are two unknowns, `u` and `u_angle`, so the following conditions should be met: +In order to perform a state estimation, the system should be observable. If the system is not observable, the calculation will either raise a not observable error or +a sparse matrix error. +In short, meeting the requirement of observability indicates that the system is either an overdetermined system (when the number of measurements is larger than the number of +unknowns) or a determined system (when the number of measurements is equal to the number of unknowns). For each node, there are two unknowns, `u` and `u_angle`, so the following conditions should be met: $$ \begin{eqnarray} @@ -72,10 +72,12 @@ Where $$ \begin{eqnarray} - n_{unknowns} & = & 2 & \cdot & n_{nodes} + n_{unknowns} & = & 2 & \cdot & n_{nodes} - 1 \end{eqnarray} $$ +Note that the phase angle is a relative value. So the number of unknowns is reduced by one. + The number of measurements can be found by taking the sum of the following: - number of nodes with a voltage sensor with magnitude only @@ -96,7 +98,7 @@ In observable systems this helps better outputting correct results. On the other ##### Necessary observability condition -Based on the requirements of observability mentioned above, user needs to satisfy at least the following conditions for state estimation calculation in power-grid-model. +Based on the requirements of observability mentioned above, user needs to satisfy at least the following conditions for state estimation calculation in `power-grid-model`. - `n_voltage_sensor >= 1` - If no voltage phasor sensors are available, then the following conditions should be satisfied: `n_unique_power_sensor >= n_bus - 1`. Otherwise: `n_unique_power_sensor + n_voltage_sensor_with_phasor >= n_bus` @@ -108,6 +110,16 @@ Based on the requirements of observability mentioned above, user needs to satisf - Any sensor on a `Branch` for all branches: Parallel branches with either side of measurements count as one. - All `Branch3` sensors. +##### Sufficient observability condition + +The condition check above only checks the necessary condition for observability. When the measurements are not independent enough, the system may still be unobservable even if the necessary condition is met. +It is rather complicated to do a full sufficient and necessary observability check in generic cases. However, `power-grid-model` performs the sufficient condition check when the following conditions are met: + +1. The system is a radial network. +2. The system does not have voltage phasor measurements. + +In this case, the validation of the independent measurements is rather straightforward. If the system is not observable, the calculation will raise a not observable error instead of sparse matrix error. + #### Short circuit calculations Short circuit calculation is carried out to analyze the worst case scenario when a fault has occurred. From 2ba10237b2a6ced46ce7bc31f9c19960d4da4cfd Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:07:21 +0100 Subject: [PATCH 37/44] Update power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp Co-authored-by: Jerry Guo <6221579+Jerry-Jinfeng-Guo@users.noreply.github.com> Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model/math_solver/observability.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp index cac259601..62a845ada 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp @@ -132,7 +132,7 @@ inline void necessary_observability_check(MeasuredValues const& measured_va // count flow sensors again Idx const n_flow_sensor_new = std::reduce(flow_sensors.cbegin(), flow_sensors.cend(), Idx{}, std::plus{}); if (n_flow_sensor_new < n_bus - 1) { - throw NotObservableError{"The number of power sensors seems to be sufficient, but they are not independent " + throw NotObservableError{"The number of power sensors appears sufficient, but they are not independent " "enough. The system is still not observable.\n"}; } } From cc93b8479fede4c1ea0b9d836f4c4d1dd0d5f46a Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:12:15 +0100 Subject: [PATCH 38/44] Apply suggestions from code review Co-authored-by: Jerry Guo <6221579+Jerry-Jinfeng-Guo@users.noreply.github.com> Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- docs/user_manual/calculations.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/user_manual/calculations.md b/docs/user_manual/calculations.md index 0501a9041..dbdc285fe 100644 --- a/docs/user_manual/calculations.md +++ b/docs/user_manual/calculations.md @@ -57,10 +57,10 @@ Output: - Power flow through branches - Deviation between measurement values and estimated state -In order to perform a state estimation, the system should be observable. If the system is not observable, the calculation will either raise a not observable error or -a sparse matrix error. +In order to perform a state estimation, the system should be observable. If the system is not observable, the calculation will raise either a `NotObservableError` or +a `SparseMatrixError`. In short, meeting the requirement of observability indicates that the system is either an overdetermined system (when the number of measurements is larger than the number of -unknowns) or a determined system (when the number of measurements is equal to the number of unknowns). For each node, there are two unknowns, `u` and `u_angle`, so the following conditions should be met: +... For each node, there are two unknowns, `u` and `u_angle`. Due to the relative nature of `u_angle` (relevant only in systems with at least two nodes), in total the following conditions should be met: $$ \begin{eqnarray} @@ -98,7 +98,7 @@ In observable systems this helps better outputting correct results. On the other ##### Necessary observability condition -Based on the requirements of observability mentioned above, user needs to satisfy at least the following conditions for state estimation calculation in `power-grid-model`. +Based on the requirements of observability mentioned above, users need to satisfy at least the following conditions for state estimation calculation in `power-grid-model`. - `n_voltage_sensor >= 1` - If no voltage phasor sensors are available, then the following conditions should be satisfied: `n_unique_power_sensor >= n_bus - 1`. Otherwise: `n_unique_power_sensor + n_voltage_sensor_with_phasor >= n_bus` @@ -118,7 +118,7 @@ It is rather complicated to do a full sufficient and necessary observability che 1. The system is a radial network. 2. The system does not have voltage phasor measurements. -In this case, the validation of the independent measurements is rather straightforward. If the system is not observable, the calculation will raise a not observable error instead of sparse matrix error. +In this case, the validation of the independent measurements is rather straightforward. If the system is not observable, the calculation will raise a `NotObservableError` instead of `SparseMatrixError`. #### Short circuit calculations From a47d83ea27da0e2841abe38995bfcb9723488c94 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:19:20 +0100 Subject: [PATCH 39/44] Update docs/user_manual/calculations.md Co-authored-by: Jerry Guo <6221579+Jerry-Jinfeng-Guo@users.noreply.github.com> Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- docs/user_manual/calculations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/user_manual/calculations.md b/docs/user_manual/calculations.md index dbdc285fe..e9d250365 100644 --- a/docs/user_manual/calculations.md +++ b/docs/user_manual/calculations.md @@ -59,8 +59,8 @@ Output: In order to perform a state estimation, the system should be observable. If the system is not observable, the calculation will raise either a `NotObservableError` or a `SparseMatrixError`. -In short, meeting the requirement of observability indicates that the system is either an overdetermined system (when the number of measurements is larger than the number of -... For each node, there are two unknowns, `u` and `u_angle`. Due to the relative nature of `u_angle` (relevant only in systems with at least two nodes), in total the following conditions should be met: +In short, meeting the requirement of observability indicates that the system is either an overdetermined system (when the number of measurements is larger than the number of unknowns. +For each node, there are two unknowns, `u` and `u_angle`. Due to the relative nature of `u_angle` (relevant only in systems with at least two nodes), in total the following conditions should be met: $$ \begin{eqnarray} From 517d6db8f0524859f44e44527784dd072458a39b Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Thu, 19 Dec 2024 09:58:09 +0100 Subject: [PATCH 40/44] resolve comments' Signed-off-by: Tony Xiang --- docs/user_manual/calculations.md | 2 -- .../include/power_grid_model/common/exception.hpp | 4 +++- .../include/power_grid_model/math_solver/observability.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/user_manual/calculations.md b/docs/user_manual/calculations.md index e9d250365..80faf0d40 100644 --- a/docs/user_manual/calculations.md +++ b/docs/user_manual/calculations.md @@ -76,8 +76,6 @@ $$ \end{eqnarray} $$ -Note that the phase angle is a relative value. So the number of unknowns is reduced by one. - The number of measurements can be found by taking the sum of the following: - number of nodes with a voltage sensor with magnitude only diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp index 7f66346c4..c97c84e93 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp @@ -106,7 +106,9 @@ class SparseMatrixError : public PowerGridError { SparseMatrixError() { append_msg("Sparse matrix error, possibly singular matrix!\n" + std::string("If you get this error from state estimation, ") + - "it usually means the system is not fully observable, i.e. not enough measurements."); + "it might mean the system is not fully observable, i.e. not enough measurements.\n" + + "It might also mean that you are running into a corner case where PGM cannot resolve yet." + + "See https://github.com/PowerGridModel/power-grid-model/issues/853."); } }; diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp index 62a845ada..4fb8cc814 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp @@ -28,7 +28,7 @@ std::tuple count_voltage_sensors(const Idx n_bus, const MeasuredValues return std::make_tuple(n_voltage_sensor, n_voltage_phasor_sensor); } -// count flow sensors into ybus structure like +// count flow sensors into integer matrix with ybus structure // lower triangle part is always zero // for diagonal part, it will be one if there is bus injection // for upper triangle part, it will be one if there is branch flow sensor and the branch is fully connected From c190e32816eb02ed3c5fbf3544a95544d3fdcf23 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Thu, 19 Dec 2024 15:51:29 +0100 Subject: [PATCH 41/44] Update power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp Co-authored-by: Martijn Govers Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model/math_solver/observability.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp index 4fb8cc814..9cd4b46ae 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp @@ -70,7 +70,8 @@ std::vector count_flow_sensors(MeasuredValues const& measured_value // this mutate the flow sensor vector to try to assign injection sensor to branch sensor // all the branch should be measured if the system is observable // this is a sufficient condition check -inline void assign_injection_sensor(YBusStructure const& y_bus_structure, std::vector& flow_sensors) { +// if the grid is not radial, the behavior is undefined. +inline void assign_injection_sensor_radial(YBusStructure const& y_bus_structure, std::vector& flow_sensors) { Idx const n_bus = std::ssize(y_bus_structure.row_indptr) - 1; // loop the row without the last bus for (Idx row = 0; row != n_bus - 1; ++row) { From 9460cd24803512e7c404b9b8adb869c5341da325 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Thu, 19 Dec 2024 15:53:46 +0100 Subject: [PATCH 42/44] Update observability.hpp Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model/math_solver/observability.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp index 9cd4b46ae..a04f9ebc5 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp @@ -129,7 +129,7 @@ inline void necessary_observability_check(MeasuredValues const& measured_va // for radial grid without phasor measurement, try to assign injection sensor to branch sensor // we can then check sufficient condition for observability if (topo.is_radial && n_voltage_phasor_sensor == 0) { - detail::assign_injection_sensor(y_bus_structure, flow_sensors); + detail::assign_injection_sensor_radial(y_bus_structure, flow_sensors); // count flow sensors again Idx const n_flow_sensor_new = std::reduce(flow_sensors.cbegin(), flow_sensors.cend(), Idx{}, std::plus{}); if (n_flow_sensor_new < n_bus - 1) { From 2372f2c6b507405dbf1cea9426edbcb040661631 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:10:17 +0100 Subject: [PATCH 43/44] Update power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp Co-authored-by: Martijn Govers Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model/math_solver/observability.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp index a04f9ebc5..0f3f46e75 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp @@ -104,6 +104,7 @@ inline void assign_injection_sensor_radial(YBusStructure const& y_bus_structure, } } // namespace detail + template inline void necessary_observability_check(MeasuredValues const& measured_values, MathModelTopology const& topo, YBusStructure const& y_bus_structure) { From 33bf4b6332ee45efe57e7f295c91ffb08b11be32 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:11:50 +0100 Subject: [PATCH 44/44] Update power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp Co-authored-by: Martijn Govers Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model/math_solver/observability.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp index 0f3f46e75..955af1b40 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/observability.hpp @@ -88,12 +88,10 @@ inline void assign_injection_sensor_radial(YBusStructure const& y_bus_structure, if (flow_sensors[branch_entry_upstream] == 0) { if (flow_sensors[bus_entry_current] == 1) { // try to steal from current bus - flow_sensors[bus_entry_current] = 0; - flow_sensors[branch_entry_upstream] = 1; + std::swap(flow_sensors[branch_entry_upstream], flow_sensors[bus_entry_current]); } else if (flow_sensors[bus_entry_upstream] == 1) { // if not possible, steal from upstream bus - flow_sensors[bus_entry_upstream] = 0; - flow_sensors[branch_entry_upstream] = 1; + std::swap(flow_sensors[branch_entry_upstream], flow_sensors[bus_entry_upstream]); } } // remove the current bus injection regardless of the original state