diff --git a/docs/user_manual/components.md b/docs/user_manual/components.md index 4a5730b1e..86e0a714a 100644 --- a/docs/user_manual/components.md +++ b/docs/user_manual/components.md @@ -569,6 +569,10 @@ with the highest probability. | ----------------- | --------- | ---- | ------------------------- | :------: | :------: | :---------------: | | `measured_object` | `int32_t` | - | ID of the measured object | ✔ | ❌ | a valid object ID | +#### Output + +A sensor only has output for state estimation. For other calculation types, sensor output is undefined. + ### Generic Voltage Sensor * type name: `generic_voltage_sensor` @@ -603,6 +607,10 @@ voltage is a line-to-line voltage. In a `asym_voltage_sensor` the measured volta ##### Steady state output +```{note} +A sensor only has output for state estimation. For other calculation types, sensor output is undefined. +``` + | name | data type | unit | description | | ------------------ | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------ | | `u_residual` | `RealValueOutput` | volt (V) | residual value between measured voltage magnitude and calculated voltage magnitude | @@ -688,6 +696,10 @@ See the documentation on [state estimation calculation methods](calculations.md# ##### Steady state output +```{note} +A sensor only has output for state estimation. For other calculation types, sensor output is undefined. +``` + | name | data type | unit | description | | ------------ | ----------------- | -------------------------- | ---------------------------------------------------------------------------- | | `p_residual` | `RealValueOutput` | watt (W) | residual value between measured active power and calculated active power | diff --git a/tests/cpp_integration_tests/CMakeLists.txt b/tests/cpp_integration_tests/CMakeLists.txt index fe3322bd1..413f4e67a 100644 --- a/tests/cpp_integration_tests/CMakeLists.txt +++ b/tests/cpp_integration_tests/CMakeLists.txt @@ -4,7 +4,6 @@ set(PROJECT_SOURCES "test_entry_point.cpp" - "test_main_model_sc.cpp" "test_main_model_se.cpp" "test_main_model.cpp" "test_math_solver.cpp" diff --git a/tests/cpp_integration_tests/test_main_model.cpp b/tests/cpp_integration_tests/test_main_model.cpp index 199991c46..6d73158ef 100644 --- a/tests/cpp_integration_tests/test_main_model.cpp +++ b/tests/cpp_integration_tests/test_main_model.cpp @@ -272,229 +272,6 @@ TEST_CASE("Test main model - power flow") { } } -TEST_CASE("Test main model - individual output (symmetric)") { - State state; - auto main_model = default_model(state); - - auto const res = main_model.calculate( - get_default_options(symmetric, CalculationMethod::newton_raphson)); - - SUBCASE("SymVoltageSensor, sym output") { // TODO(mgovers): sensor output for powerflow calculations are not tested - // elsewhere => validation case - main_model.output_result(res, state.sym_node); - main_model.output_result(res, state.sym_voltage_sensor); - - CHECK(state.sym_voltage_sensor[0].u_residual == doctest::Approx(1.01 * 10.0e3 - state.sym_node[0].u)); - CHECK(state.sym_voltage_sensor[1].u_residual == doctest::Approx(1.02 * 10.0e3 - state.sym_node[1].u)); - CHECK(state.sym_voltage_sensor[0].u_angle_residual == doctest::Approx(0.1 - state.sym_node[0].u_angle)); - CHECK(state.sym_voltage_sensor[1].u_angle_residual == doctest::Approx(0.2 - state.sym_node[1].u_angle)); - } - - SUBCASE("SymPowerSensor, sym output") { // TODO(mgovers): sensor output for powerflow calculations are not tested - // elsewhere => validation case - main_model.output_result(res, state.sym_line); - main_model.output_result(res, state.sym_link); - main_model.output_result(res, state.sym_source); - main_model.output_result(res, state.sym_load_sym); - main_model.output_result(res, state.sym_load_asym); - main_model.output_result(res, state.sym_shunt); - main_model.output_result(res, state.sym_power_sensor); - - CHECK(state.sym_power_sensor[0].p_residual == doctest::Approx(1.1e6 - state.sym_line[0].p_from)); - CHECK(state.sym_power_sensor[0].q_residual == doctest::Approx(1.1e3 - state.sym_line[0].q_from)); - CHECK(state.sym_power_sensor[1].p_residual == doctest::Approx(1.3e6 - state.sym_source[0].p)); - CHECK(state.sym_power_sensor[1].q_residual == doctest::Approx(1.3e3 - state.sym_source[0].q)); - CHECK(state.sym_power_sensor[2].p_residual == doctest::Approx(1.4e6 - state.sym_source[0].p)); - CHECK(state.sym_power_sensor[2].q_residual == doctest::Approx(1.4e3 - state.sym_source[0].q)); - CHECK(state.sym_power_sensor[3].p_residual == doctest::Approx(1.5e6 - state.sym_shunt[0].p)); - CHECK(state.sym_power_sensor[3].q_residual == doctest::Approx(1.5e3 - state.sym_shunt[0].q)); - CHECK(state.sym_power_sensor[4].p_residual == doctest::Approx(1.6e6 - state.sym_load_sym[0].p)); - CHECK(state.sym_power_sensor[4].q_residual == doctest::Approx(1.6e3 - state.sym_load_sym[0].q)); - CHECK(state.sym_power_sensor[5].p_residual == doctest::Approx(1.7e6 - state.sym_load_asym[0].p)); - CHECK(state.sym_power_sensor[5].q_residual == doctest::Approx(1.7e3 - state.sym_load_asym[0].q)); - CHECK(state.sym_power_sensor[6].p_residual == - doctest::Approx(3.0e6 - (state.sym_source[1].p - state.sym_load_sym[0].p - state.sym_load_asym[0].p))); - CHECK(state.sym_power_sensor[6].q_residual == - doctest::Approx(3.0e3 - (state.sym_source[1].q - state.sym_load_sym[0].q - state.sym_load_asym[0].q))); - } - - SUBCASE("AsymVoltageSensor, sym output") { // TODO(mgovers): sensor output for powerflow calculations are not tested - // elsewhere => validation case - main_model.output_result(res, state.sym_node); - main_model.output_result(res, state.asym_voltage_sensor_sym_output); - - CHECK(state.asym_voltage_sensor_sym_output[0].u_residual == doctest::Approx(10.32e3 - state.sym_node[2].u)); - CHECK(state.asym_voltage_sensor_sym_output[0].u_angle_residual == - doctest::Approx(0.0 - state.sym_node[2].u_angle)); - } - - SUBCASE("AsymPowerSensor, sym output") { // TODO(mgovers): sensor output for powerflow calculations are not tested - // elsewhere => validation case - main_model.output_result(res, state.sym_line); - main_model.output_result(res, state.sym_link); - main_model.output_result(res, state.sym_source); - main_model.output_result(res, state.sym_load_sym); - main_model.output_result(res, state.sym_load_asym); - main_model.output_result(res, state.sym_shunt); - main_model.output_result(res, state.asym_power_sensor_sym_output); - - CHECK(state.asym_power_sensor_sym_output[0].p_residual == - doctest::Approx(3 * 2.12e6 - state.sym_line[0].p_from)); - CHECK(state.asym_power_sensor_sym_output[0].q_residual == - doctest::Approx(3 * 2.12e3 - state.sym_line[0].q_from)); - CHECK(state.asym_power_sensor_sym_output[1].p_residual == doctest::Approx(3 * 2.32e6 - state.sym_source[0].p)); - CHECK(state.asym_power_sensor_sym_output[1].q_residual == doctest::Approx(3 * 2.32e3 - state.sym_source[0].q)); - CHECK(state.asym_power_sensor_sym_output[2].p_residual == doctest::Approx(3 * 2.42e6 - state.sym_source[0].p)); - CHECK(state.asym_power_sensor_sym_output[2].q_residual == doctest::Approx(3 * 2.42e3 - state.sym_source[0].q)); - CHECK(state.asym_power_sensor_sym_output[3].p_residual == doctest::Approx(3 * 2.52e6 - state.sym_shunt[0].p)); - CHECK(state.asym_power_sensor_sym_output[3].q_residual == doctest::Approx(3 * 2.52e3 - state.sym_shunt[0].q)); - CHECK(state.asym_power_sensor_sym_output[4].p_residual == - doctest::Approx(3 * 2.62e6 - state.sym_load_sym[0].p)); - CHECK(state.asym_power_sensor_sym_output[4].q_residual == - doctest::Approx(3 * 2.62e3 - state.sym_load_sym[0].q)); - CHECK(state.asym_power_sensor_sym_output[5].p_residual == - doctest::Approx(3 * 2.72e6 - state.sym_load_asym[0].p)); - CHECK(state.asym_power_sensor_sym_output[5].q_residual == - doctest::Approx(3 * 2.72e3 - state.sym_load_asym[0].q)); - CHECK( - state.asym_power_sensor_sym_output[6].p_residual == - doctest::Approx(3 * 5.02e6 - (state.sym_source[1].p - state.sym_load_sym[0].p - state.sym_load_asym[0].p))); - CHECK( - state.asym_power_sensor_sym_output[6].q_residual == - doctest::Approx(3 * 5.02e3 - (state.sym_source[1].q - state.sym_load_sym[0].q - state.sym_load_asym[0].q))); - } -} - -TEST_CASE("Test main model - individual output (asymmetric)") { - State state; - auto main_model = default_model(state); - - auto const res = main_model.calculate( - get_default_options(asymmetric, CalculationMethod::newton_raphson)); - - SUBCASE("AsymVoltageSensor, asym output") { // TODO(mgovers): sensor output for powerflow calculations are not - // tested elsewhere => validation case - main_model.output_result(res, state.asym_node); - main_model.output_result(res, state.asym_voltage_sensor); - - CHECK(state.asym_voltage_sensor[0].u_residual[0] == - doctest::Approx(1.031 / sqrt3 * 10.0e3 - state.asym_node[2].u[0])); - CHECK(state.asym_voltage_sensor[0].u_residual[1] == - doctest::Approx(1.032 / sqrt3 * 10.0e3 - state.asym_node[2].u[1])); - CHECK(state.asym_voltage_sensor[0].u_residual[2] == - doctest::Approx(1.033 / sqrt3 * 10.0e3 - state.asym_node[2].u[2])); - CHECK(state.asym_voltage_sensor[0].u_angle_residual[0] == doctest::Approx(0.0 - state.asym_node[2].u_angle[0])); - CHECK(state.asym_voltage_sensor[0].u_angle_residual[1] == - doctest::Approx(-deg_120 - state.asym_node[2].u_angle[1])); - CHECK(state.asym_voltage_sensor[0].u_angle_residual[2] == - doctest::Approx(-deg_240 - state.asym_node[2].u_angle[2])); - } - - SUBCASE("SymVoltageSensor, asym output") { // TODO(mgovers): sensor output for powerflow calculations are not tested - // elsewhere => validation case - main_model.output_result(res, state.asym_node); - main_model.output_result(res, state.sym_voltage_sensor_asym_output); - - CHECK(state.sym_voltage_sensor_asym_output[0].u_residual[0] == - doctest::Approx(10.1e3 / sqrt3 - state.asym_node[0].u[0])); - CHECK(state.sym_voltage_sensor_asym_output[0].u_residual[1] == - doctest::Approx(10.1e3 / sqrt3 - state.asym_node[0].u[1])); - CHECK(state.sym_voltage_sensor_asym_output[0].u_residual[2] == - doctest::Approx(10.1e3 / sqrt3 - state.asym_node[0].u[2])); - CHECK(state.sym_voltage_sensor_asym_output[0].u_angle_residual[0] == - doctest::Approx(0.1 - state.asym_node[0].u_angle[0])); - CHECK(state.sym_voltage_sensor_asym_output[0].u_angle_residual[1] == - doctest::Approx(0.1 - state.asym_node[0].u_angle[1])); - CHECK(state.sym_voltage_sensor_asym_output[0].u_angle_residual[2] == - doctest::Approx(0.1 - state.asym_node[0].u_angle[2])); - CHECK(state.sym_voltage_sensor_asym_output[1].u_residual[0] == - doctest::Approx(10.2e3 / sqrt3 - state.asym_node[1].u[0])); - CHECK(state.sym_voltage_sensor_asym_output[1].u_residual[1] == - doctest::Approx(10.2e3 / sqrt3 - state.asym_node[1].u[1])); - CHECK(state.sym_voltage_sensor_asym_output[1].u_residual[2] == - doctest::Approx(10.2e3 / sqrt3 - state.asym_node[1].u[2])); - CHECK(state.sym_voltage_sensor_asym_output[1].u_angle_residual[0] == - doctest::Approx(0.2 - state.asym_node[1].u_angle[0])); - CHECK(state.sym_voltage_sensor_asym_output[1].u_angle_residual[1] == - doctest::Approx(0.2 - state.asym_node[1].u_angle[1])); - CHECK(state.sym_voltage_sensor_asym_output[1].u_angle_residual[2] == - doctest::Approx(0.2 - state.asym_node[1].u_angle[2])); - } - - // Note that only 1/3 of the values is being checked - SUBCASE("AsymPowerSensor, asym output") { // TODO(mgovers): sensor output for powerflow calculations are not tested - // elsewhere - main_model.output_result(res, state.asym_line); - main_model.output_result(res, state.asym_link); - main_model.output_result(res, state.asym_source); - main_model.output_result(res, state.asym_load_sym); - main_model.output_result(res, state.asym_load_asym); - main_model.output_result(res, state.asym_shunt); - main_model.output_result(res, state.asym_power_sensor); - - CHECK(state.asym_power_sensor[0].p_residual[0] == doctest::Approx(2.11e6 - state.asym_line[0].p_from[0])); - CHECK(state.asym_power_sensor[0].q_residual[1] == doctest::Approx(2.12e3 - state.asym_line[0].q_from[1])); - CHECK(state.asym_power_sensor[1].p_residual[1] == doctest::Approx(2.32e6 - state.asym_source[0].p[1])); - CHECK(state.asym_power_sensor[1].q_residual[2] == doctest::Approx(2.33e3 - state.asym_source[0].q[2])); - CHECK(state.asym_power_sensor[2].p_residual[0] == doctest::Approx(2.41e6 - state.asym_source[0].p[0])); - CHECK(state.asym_power_sensor[2].q_residual[1] == doctest::Approx(2.42e3 - state.asym_source[0].q[1])); - CHECK(state.asym_power_sensor[3].p_residual[2] == doctest::Approx(2.53e6 - state.asym_shunt[0].p[2])); - CHECK(state.asym_power_sensor[3].q_residual[0] == doctest::Approx(2.51e3 - state.asym_shunt[0].q[0])); - CHECK(state.asym_power_sensor[4].p_residual[1] == doctest::Approx(2.62e6 - state.asym_load_sym[0].p[1])); - CHECK(state.asym_power_sensor[4].q_residual[2] == doctest::Approx(2.63e3 - state.asym_load_sym[0].q[2])); - CHECK(state.asym_power_sensor[5].p_residual[0] == doctest::Approx(2.71e6 - state.asym_load_asym[0].p[0])); - CHECK(state.asym_power_sensor[5].q_residual[1] == doctest::Approx(2.72e3 - state.asym_load_asym[0].q[1])); - CHECK(state.asym_power_sensor[6].p_residual[0] == - doctest::Approx( - 5.01e6 - (state.asym_source[1].p[0] - state.asym_load_sym[0].p[0] - state.asym_load_asym[0].p[0]))); - CHECK(state.asym_power_sensor[6].q_residual[1] == - doctest::Approx( - 5.02e3 - (state.asym_source[1].q[1] - state.asym_load_sym[0].q[1] - state.asym_load_asym[0].q[1]))); - } - - SUBCASE("SymPowerSensor, asym output") { // TODO(mgovers): sensor output for powerflow calculations are not tested - // elsewhere - main_model.output_result(res, state.asym_line); - main_model.output_result(res, state.asym_link); - main_model.output_result(res, state.asym_source); - main_model.output_result(res, state.asym_load_sym); - main_model.output_result(res, state.asym_load_asym); - main_model.output_result(res, state.asym_shunt); - main_model.output_result(res, state.sym_power_sensor_asym_output); - - CHECK(state.sym_power_sensor_asym_output[0].p_residual[0] == - doctest::Approx(1.1e6 / 3 - state.asym_line[0].p_from[0])); - CHECK(state.sym_power_sensor_asym_output[0].q_residual[1] == - doctest::Approx(1.1e3 / 3 - state.asym_line[0].q_from[1])); - CHECK(state.sym_power_sensor_asym_output[1].p_residual[1] == - doctest::Approx(1.3e6 / 3 - state.asym_source[0].p[1])); - CHECK(state.sym_power_sensor_asym_output[1].q_residual[2] == - doctest::Approx(1.3e3 / 3 - state.asym_source[0].q[2])); - CHECK(state.sym_power_sensor_asym_output[2].p_residual[0] == - doctest::Approx(1.4e6 / 3 - state.asym_source[0].p[0])); - CHECK(state.sym_power_sensor_asym_output[2].q_residual[1] == - doctest::Approx(1.4e3 / 3 - state.asym_source[0].q[1])); - CHECK(state.sym_power_sensor_asym_output[3].p_residual[2] == - doctest::Approx(1.5e6 / 3 - state.asym_shunt[0].p[2])); - CHECK(state.sym_power_sensor_asym_output[3].q_residual[0] == - doctest::Approx(1.5e3 / 3 - state.asym_shunt[0].q[0])); - CHECK(state.sym_power_sensor_asym_output[4].p_residual[1] == - doctest::Approx(1.6e6 / 3 - state.asym_load_sym[0].p[1])); - CHECK(state.sym_power_sensor_asym_output[4].q_residual[2] == - doctest::Approx(1.6e3 / 3 - state.asym_load_sym[0].q[2])); - CHECK(state.sym_power_sensor_asym_output[5].p_residual[0] == - doctest::Approx(1.7e6 / 3 - state.asym_load_asym[0].p[0])); - CHECK(state.sym_power_sensor_asym_output[5].q_residual[1] == - doctest::Approx(1.7e3 / 3 - state.asym_load_asym[0].q[1])); - CHECK(state.sym_power_sensor_asym_output[6].p_residual[0] == - doctest::Approx(3.0e6 / 3 - (state.asym_source[1].p[0] - state.asym_load_sym[0].p[0] - - state.asym_load_asym[0].p[0]))); - CHECK(state.sym_power_sensor_asym_output[6].q_residual[1] == - doctest::Approx(3.0e3 / 3 - (state.asym_source[1].q[1] - state.asym_load_sym[0].q[1] - - state.asym_load_asym[0].q[1]))); - } -} - TEST_CASE_TEMPLATE("Test main model - unknown id", settings, regular_update, cached_update) { // TODO(mgovers): we need this test State const state; @@ -777,7 +554,7 @@ TEST_CASE_TEMPLATE("Test main model - restore components", settings, regular_upd } TEST_CASE_TEMPLATE("Test main model - updates w/ alternating compute mode", settings, regular_update, - cached_update) { // TODO(mgovers): we need to keep this one way or another + cached_update) { // TODO(mgovers): move to api tests; not possible with current validation framework constexpr auto check_sym = [](MainModel const& model_, auto const& math_output_) { State state_; model_.output_result(math_output_, state_.sym_node); @@ -860,373 +637,6 @@ TEST_CASE_TEMPLATE("Test main model - updates w/ alternating compute mode", sett main_model.restore_components(update_data); } -TEST_CASE("Test main model - runtime dispatch") { - using CalculationMethod::newton_raphson; - - State state; - auto main_model = default_model(state); - - ConstDataset input_data{false, 1, "input", meta_data::meta_data_gen::meta_data}; - input_data.add_buffer("node", state.node_input.size(), state.node_input.size(), nullptr, state.node_input.data()); - input_data.add_buffer("line", state.line_input.size(), state.line_input.size(), nullptr, state.line_input.data()); - input_data.add_buffer("link", state.link_input.size(), state.link_input.size(), nullptr, state.link_input.data()); - input_data.add_buffer("source", state.source_input.size(), state.source_input.size(), nullptr, - state.source_input.data()); - input_data.add_buffer("sym_load", state.sym_load_input.size(), state.sym_load_input.size(), nullptr, - state.sym_load_input.data()); - input_data.add_buffer("asym_load", state.asym_load_input.size(), state.asym_load_input.size(), nullptr, - state.asym_load_input.data()); - input_data.add_buffer("shunt", state.shunt_input.size(), state.shunt_input.size(), nullptr, - state.shunt_input.data()); - - SUBCASE("Single-size batches") { // TODO(mgovers): can be removed: validation cases already do single + - // multi-threaded; whitebox test should be isolated - ConstDataset update_data{true, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), nullptr, - state.sym_load_update.data()); - update_data.add_buffer("asym_load", state.asym_load_update.size(), state.asym_load_update.size(), nullptr, - state.asym_load_update.data()); - update_data.add_buffer("shunt", state.shunt_update.size(), state.shunt_update.size(), nullptr, - state.shunt_update.data()); - update_data.add_buffer("source", state.source_update.size(), state.source_update.size(), nullptr, - state.source_update.data()); - update_data.add_buffer("link", state.link_update.size(), state.link_update.size(), nullptr, - state.link_update.data()); - - MutableDataset sym_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - sym_result_data.add_buffer("node", state.sym_node.size(), state.sym_node.size(), nullptr, - state.sym_node.data()); - sym_result_data.add_buffer("line", state.sym_line.size(), state.sym_line.size(), nullptr, - state.sym_line.data()); - sym_result_data.add_buffer("link", state.sym_link.size(), state.sym_link.size(), nullptr, - state.sym_link.data()); - sym_result_data.add_buffer("source", state.sym_source.size(), state.sym_source.size(), nullptr, - state.sym_source.data()); - sym_result_data.add_buffer("sym_load", state.sym_load_sym.size(), state.sym_load_sym.size(), nullptr, - state.sym_load_sym.data()); - sym_result_data.add_buffer("asym_load", state.sym_load_asym.size(), state.sym_load_asym.size(), nullptr, - state.sym_load_asym.data()); - sym_result_data.add_buffer("shunt", state.sym_shunt.size(), state.sym_shunt.size(), nullptr, - state.sym_shunt.data()); - - MutableDataset asym_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; - asym_result_data.add_buffer("node", state.asym_node.size(), state.asym_node.size(), nullptr, - state.asym_node.data()); - - MainModel model{50.0, input_data}; - - // calculation - model.calculate(get_default_options(symmetric, newton_raphson), sym_result_data); - CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[1].u_pu == doctest::Approx(test::u1)); - CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); - CHECK(state.sym_line[0].i_from == doctest::Approx(test::i)); - CHECK(state.sym_link[0].i_from == doctest::Approx(test::i)); - CHECK(state.sym_source[0].i == doctest::Approx(test::i)); - CHECK(state.sym_source[1].i == doctest::Approx(0.0)); - CHECK(state.sym_load_sym[0].i == doctest::Approx(test::i_load)); - CHECK(state.sym_load_asym[0].i == doctest::Approx(test::i_load)); - CHECK(state.sym_shunt[0].i == doctest::Approx(test::i_shunt)); - model.calculate(get_default_options(asymmetric, newton_raphson), asym_result_data); - CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); - CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(test::u1)); - CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); - - // update and calculation - model.update_components(update_data); - model.calculate(get_default_options(symmetric, newton_raphson), sym_result_data); - CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[1].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); - model.calculate(get_default_options(asymmetric, newton_raphson), asym_result_data); - CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); - CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(1.05)); - CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); - - // test batch calculation - model = MainModel{50.0, input_data}; - // symmetric sequential - model.calculate(get_default_options(symmetric, newton_raphson), sym_result_data, update_data); - CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[1].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); - // symmetric parallel - model.calculate(get_default_options(symmetric, newton_raphson, 0), sym_result_data, update_data); - CHECK(state.sym_node[0].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[1].u_pu == doctest::Approx(1.05)); - CHECK(state.sym_node[2].u_pu == doctest::Approx(test::u1)); - // asymmetric sequential - model.calculate(get_default_options(asymmetric, newton_raphson), asym_result_data, update_data); - CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); - CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(1.05)); - CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); - // asymmetric parallel - model.calculate(get_default_options(asymmetric, newton_raphson, 0), asym_result_data, update_data); - CHECK(state.asym_node[0].u_pu(0) == doctest::Approx(1.05)); - CHECK(state.asym_node[1].u_pu(1) == doctest::Approx(1.05)); - CHECK(state.asym_node[2].u_pu(2) == doctest::Approx(test::u1)); - } - - SUBCASE("no dependent updates within batches") { // TODO(mgovers): validation cases capture this; whitebox test - // should be isolated - MainModel model{50.0, input_data}; - std::vector sym_load_update_2{{7, 1, nan, 1.0e7}, {7, 1, 1.0e3, nan}, {7, 1, 1.0e3, 1.0e7}}; - - ConstDataset dependent_update_data{true, static_cast(sym_load_update_2.size()), "update", - meta_data::meta_data_gen::meta_data}; - MutableDataset dependent_result_data{true, static_cast(sym_load_update_2.size()), "sym_output", - meta_data::meta_data_gen::meta_data}; - - dependent_update_data.add_buffer("sym_load", 1, sym_load_update_2.size(), nullptr, sym_load_update_2.data()); - - std::vector> sym_node_2(sym_load_update_2.size() * state.sym_node.size()); - dependent_result_data.add_buffer("node", state.sym_node.size(), sym_node_2.size(), nullptr, sym_node_2.data()); - - model.calculate(get_default_options(symmetric, newton_raphson), dependent_result_data, dependent_update_data); - CHECK(sym_node_2[0].u_pu == doctest::Approx(1.05)); - CHECK(sym_node_2[1].u_pu == doctest::Approx(0.66).epsilon(0.005)); - CHECK(sym_node_2[2].u_pu == doctest::Approx(0.66).epsilon(0.005)); - CHECK(sym_node_2[3].u_pu == doctest::Approx(1.05)); - CHECK(sym_node_2[4].u_pu == doctest::Approx(0.87).epsilon(0.005)); - CHECK(sym_node_2[5].u_pu == doctest::Approx(0.87).epsilon(0.005)); - CHECK(sym_node_2[6].u_pu == doctest::Approx(1.05)); - CHECK(sym_node_2[7].u_pu == doctest::Approx(0.67).epsilon(0.005)); - CHECK(sym_node_2[8].u_pu == doctest::Approx(0.67).epsilon(0.005)); - } - - SUBCASE("Columnar buffers in dataset") { // TODO(mgovers): API tests now capture this; validation cases do not test - // columnar buffers - auto const options = get_default_options(symmetric, CalculationMethod::newton_raphson); - - SUBCASE("Columnar buffers in input data") { - std::vector node_ids; - std::vector node_u_rated; - std::ranges::transform(state.node_input, std::back_inserter(node_ids), - [](auto const& node) { return node.id; }); - std::ranges::transform(state.node_input, std::back_inserter(node_u_rated), - [](auto const& node) { return node.u_rated; }); - REQUIRE(node_ids.size() == node_u_rated.size()); - - ConstDataset input_data_with_columns{false, 1, "input", meta_data::meta_data_gen::meta_data}; - input_data_with_columns.add_buffer("node", state.node_input.size(), state.node_input.size(), nullptr, - nullptr); - input_data_with_columns.add_attribute_buffer("node", "id", node_ids.data()); - input_data_with_columns.add_attribute_buffer("node", "u_rated", node_u_rated.data()); - input_data_with_columns.add_buffer("line", state.line_input.size(), state.line_input.size(), nullptr, - state.line_input.data()); - input_data_with_columns.add_buffer("link", state.link_input.size(), state.link_input.size(), nullptr, - state.link_input.data()); - input_data_with_columns.add_buffer("source", state.source_input.size(), state.source_input.size(), nullptr, - state.source_input.data()); - input_data_with_columns.add_buffer("sym_load", state.sym_load_input.size(), state.sym_load_input.size(), - nullptr, state.sym_load_input.data()); - input_data_with_columns.add_buffer("asym_load", state.asym_load_input.size(), state.asym_load_input.size(), - nullptr, state.asym_load_input.data()); - input_data_with_columns.add_buffer("shunt", state.shunt_input.size(), state.shunt_input.size(), nullptr, - state.shunt_input.data()); - - MainModel row_based_model{50.0, input_data}; - MainModel columnar_model{50.0, input_data_with_columns}; - - std::vector node_output_from_row_based(state.node_input.size()); - std::vector node_output_from_columnar(node_ids.size()); - - MutableDataset sym_output_from_row_based{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - sym_output_from_row_based.add_buffer("node", node_output_from_row_based.size(), - node_output_from_row_based.size(), nullptr, - node_output_from_row_based.data()); - MutableDataset sym_output_from_columnar{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - sym_output_from_columnar.add_buffer("node", node_output_from_columnar.size(), - node_output_from_columnar.size(), nullptr, - node_output_from_columnar.data()); - - row_based_model.calculate(options, sym_output_from_row_based); - columnar_model.calculate(options, sym_output_from_columnar); - - REQUIRE(node_output_from_columnar.size() == node_output_from_row_based.size()); - - for (Idx idx = 0; idx < std::ssize(node_output_from_columnar); ++idx) { - CHECK(node_output_from_columnar[idx].id == node_output_from_row_based[idx].id); - CHECK(node_output_from_columnar[idx].u_pu == node_output_from_row_based[idx].u_pu); - } - } - - SUBCASE("Columnar buffers in output data") { - MainModel model{50.0, input_data}; - - std::vector row_based_node_output(state.node_input.size()); - std::vector columnar_node_output_id(state.node_input.size()); - std::vector columnar_node_output_u_pu(state.node_input.size()); - - MutableDataset row_based_sym_output{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - row_based_sym_output.add_buffer("node", row_based_node_output.size(), row_based_node_output.size(), nullptr, - row_based_node_output.data()); - MutableDataset columnar_sym_output{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - columnar_sym_output.add_buffer("node", row_based_node_output.size(), row_based_node_output.size(), nullptr, - nullptr); - columnar_sym_output.add_attribute_buffer("node", "id", columnar_node_output_id.data()); - columnar_sym_output.add_attribute_buffer("node", "u_pu", columnar_node_output_u_pu.data()); - - model.calculate(options, row_based_sym_output); - model.calculate(options, columnar_sym_output); - - REQUIRE(columnar_node_output_id.size() == row_based_node_output.size()); - REQUIRE(columnar_node_output_u_pu.size() == row_based_node_output.size()); - - for (Idx idx = 0; idx < std::ssize(columnar_node_output_id); ++idx) { - CHECK(columnar_node_output_id[idx] == row_based_node_output[idx].id); - CHECK(columnar_node_output_u_pu[idx] == doctest::Approx(row_based_node_output[idx].u_pu)); - } - } - - SUBCASE("Columnar buffers in update data") { - std::vector sym_load_ids; - std::vector sym_load_p_specified; - std::ranges::transform(state.sym_load_update, std::back_inserter(sym_load_p_specified), - [](auto const& sym_load) { return sym_load.p_specified; }); - - std::ranges::transform(state.sym_load_update, std::back_inserter(sym_load_ids), - [](auto const& sym_load) { return sym_load.id; }); - REQUIRE(sym_load_ids.size() == sym_load_p_specified.size()); - REQUIRE(sym_load_p_specified.size() == state.sym_load_update.size()); - - auto const update_size = sym_load_ids.size(); - - SUBCASE("With IDs") { - ConstDataset update_data_with_rows{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data_with_rows.add_buffer("sym_load", state.sym_load_update.size(), state.sym_load_update.size(), - nullptr, state.sym_load_update.data()); - - ConstDataset update_data_with_columns{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data_with_columns.add_buffer("sym_load", update_size, update_size, nullptr, nullptr); - update_data_with_columns.add_attribute_buffer("sym_load", "id", sym_load_ids.data()); - update_data_with_columns.add_attribute_buffer("sym_load", "p_specified", sym_load_p_specified.data()); - - MainModel base_model{50.0, input_data}; - MainModel row_based_model{base_model}; - MainModel columnar_model{base_model}; - row_based_model.update_components(update_data_with_rows); - columnar_model.update_components(update_data_with_columns); - - std::vector node_output_from_base(state.node_input.size()); - std::vector node_output_from_row_based(state.node_input.size()); - std::vector node_output_from_columnar(state.node_input.size()); - - MutableDataset sym_output_from_base{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - sym_output_from_base.add_buffer("node", node_output_from_base.size(), node_output_from_base.size(), - nullptr, node_output_from_base.data()); - MutableDataset sym_output_from_row_based{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - sym_output_from_row_based.add_buffer("node", node_output_from_row_based.size(), - node_output_from_row_based.size(), nullptr, - node_output_from_row_based.data()); - MutableDataset sym_output_from_columnar{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - sym_output_from_columnar.add_buffer("node", node_output_from_columnar.size(), - node_output_from_columnar.size(), nullptr, - node_output_from_columnar.data()); - - base_model.calculate(options, sym_output_from_base); - row_based_model.calculate(options, sym_output_from_row_based); - columnar_model.calculate(options, sym_output_from_columnar); - - REQUIRE(node_output_from_columnar.size() == node_output_from_base.size()); - REQUIRE(node_output_from_columnar.size() == node_output_from_row_based.size()); - - for (Idx idx = 0; idx < std::ssize(node_output_from_columnar); ++idx) { - // check columnar updates work same way as row-based updates - CHECK(node_output_from_columnar[idx].id == doctest::Approx(node_output_from_row_based[idx].id)); - CHECK(node_output_from_columnar[idx].u_pu == doctest::Approx(node_output_from_row_based[idx].u_pu)); - // check update actually changed something - CHECK(node_output_from_columnar[idx].id == doctest::Approx(node_output_from_base[idx].id)); - if (idx == 0) { // sym_load node - CHECK(node_output_from_columnar[idx].u_pu == doctest::Approx(node_output_from_base[idx].u_pu)); - } else { - CHECK(node_output_from_columnar[idx].u_pu != doctest::Approx(node_output_from_base[idx].u_pu)); - } - } - } - - SUBCASE("Without IDs") { // TODO(mgovers): API tests already captured this; validation cases added - ConstDataset update_data_with_ids{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data_with_ids.add_buffer("sym_load", update_size, update_size, nullptr, nullptr); - update_data_with_ids.add_attribute_buffer("sym_load", "id", sym_load_ids.data()); - update_data_with_ids.add_attribute_buffer("sym_load", "p_specified", sym_load_p_specified.data()); - - ConstDataset update_data_without_ids{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data_without_ids.add_buffer("sym_load", update_size, update_size, nullptr, nullptr); - update_data_without_ids.add_attribute_buffer("sym_load", "p_specified", sym_load_p_specified.data()); - - MainModel const base_model{50.0, input_data}; - MainModel columnar_model_w_id{base_model}; - MainModel columnar_model_wo_id{base_model}; - - columnar_model_w_id.update_components(update_data_with_ids); - columnar_model_wo_id.update_components(update_data_without_ids); - - std::vector node_output_columnar_w_id(state.node_input.size()); - std::vector node_output_columnar_wo_id(state.node_input.size()); - - MutableDataset sym_output_columnar_w_id{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - sym_output_columnar_w_id.add_buffer("node", node_output_columnar_w_id.size(), - node_output_columnar_w_id.size(), nullptr, - node_output_columnar_w_id.data()); - MutableDataset sym_output_columnar_wo_id{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - sym_output_columnar_wo_id.add_buffer("node", node_output_columnar_wo_id.size(), - node_output_columnar_wo_id.size(), nullptr, - node_output_columnar_wo_id.data()); - - columnar_model_w_id.calculate(options, sym_output_columnar_w_id); - columnar_model_wo_id.calculate(options, sym_output_columnar_wo_id); - - REQUIRE(node_output_columnar_wo_id.size() == node_output_columnar_w_id.size()); - REQUIRE(node_output_columnar_wo_id.size() == node_output_columnar_w_id.size()); - - for (Idx idx = 0; idx < std::ssize(node_output_columnar_w_id); ++idx) { - // check columnar updates without ids work same way as ones with ids - CHECK(node_output_columnar_wo_id[idx].id == doctest::Approx(node_output_columnar_w_id[idx].id)); - CHECK(node_output_columnar_wo_id[idx].u_pu == doctest::Approx(node_output_columnar_w_id[idx].u_pu)); - } - } - } - - SUBCASE( - "Empty columnar update data") { // TODO(mgovers): power_flow/dummy-test-batch-shunt already captures this - std::vector sym_load_ids; - std::vector sym_load_p_specified; - REQUIRE(sym_load_ids.size() == sym_load_p_specified.size()); - - ConstDataset update_data_with_columns{false, 1, "update", meta_data::meta_data_gen::meta_data}; - update_data_with_columns.add_buffer("sym_load", sym_load_ids.size(), sym_load_ids.size(), nullptr, nullptr); - update_data_with_columns.add_attribute_buffer("sym_load", "id", sym_load_ids.data()); - update_data_with_columns.add_attribute_buffer("sym_load", "p_specified", sym_load_p_specified.data()); - - MainModel base_model{50.0, input_data}; - MainModel columnar_model{base_model}; - columnar_model.update_components(update_data_with_columns); - - std::vector node_output_from_base(state.node_input.size()); - std::vector node_output_from_columnar(state.node_input.size()); - - MutableDataset sym_output_from_base{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - sym_output_from_base.add_buffer("node", node_output_from_base.size(), node_output_from_base.size(), nullptr, - node_output_from_base.data()); - MutableDataset sym_output_from_columnar{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - sym_output_from_columnar.add_buffer("node", node_output_from_columnar.size(), - node_output_from_columnar.size(), nullptr, - node_output_from_columnar.data()); - - base_model.calculate(options, sym_output_from_base); - columnar_model.calculate(options, sym_output_from_columnar); - - REQUIRE(node_output_from_columnar.size() == node_output_from_base.size()); - - for (Idx idx = 0; idx < std::ssize(node_output_from_base); ++idx) { - CHECK(node_output_from_columnar[idx].id == doctest::Approx(node_output_from_base[idx].id)); - CHECK(node_output_from_columnar[idx].u_pu == doctest::Approx(node_output_from_base[idx].u_pu)); - } - } - } -} - namespace { auto incomplete_input_model(State const& state) -> MainModel { MainModel main_model{50.0, meta_data::meta_data_gen::meta_data}; @@ -1288,41 +698,6 @@ TEST_CASE("Test main model - incomplete input") { MainModel const ref_model{main_model}; - SUBCASE("Symmetrical - Complete") { // TODO(mgovers): validation case with different values in - // power_flow/dummy-test-batch-incomplete-input - MutableDataset test_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - MutableDataset ref_result_data{true, 1, "sym_output", meta_data::meta_data_gen::meta_data}; - - std::vector> test_sym_node(state.sym_node.size()); - std::vector> ref_sym_node(state.sym_node.size()); - test_result_data.add_buffer("node", test_sym_node.size(), test_sym_node.size(), nullptr, test_sym_node.data()); - ref_result_data.add_buffer("node", ref_sym_node.size(), ref_sym_node.size(), nullptr, ref_sym_node.data()); - - SUBCASE("Test linear calculation") { - test_model.calculate(get_default_options(symmetric, linear), test_result_data, update_data); - main_model.calculate(get_default_options(symmetric, linear), ref_result_data, update_data); - } - - SUBCASE("Test linear current calculation") { - test_model.calculate(get_default_options(symmetric, linear_current), test_result_data, update_data); - main_model.calculate(get_default_options(symmetric, linear_current), ref_result_data, update_data); - } - - SUBCASE("Test iterative current calculation") { - test_model.calculate(get_default_options(symmetric, iterative_current), test_result_data, update_data); - main_model.calculate(get_default_options(symmetric, iterative_current), ref_result_data, update_data); - } - - SUBCASE("Test iterative Newton-Raphson calculation") { - test_model.calculate(get_default_options(symmetric, newton_raphson), test_result_data, update_data); - main_model.calculate(get_default_options(symmetric, newton_raphson), ref_result_data, update_data); - } - - CHECK(test_sym_node[0].u_pu == doctest::Approx(ref_sym_node[0].u_pu)); - CHECK(test_sym_node[1].u_pu == doctest::Approx(ref_sym_node[1].u_pu)); - CHECK(test_sym_node[2].u_pu == doctest::Approx(ref_sym_node[2].u_pu)); - } - SUBCASE("Asymmetrical - Complete") { // TODO(mgovers): no validation case for asym exists MutableDataset test_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; MutableDataset ref_result_data{true, 1, "asym_output", meta_data::meta_data_gen::meta_data}; diff --git a/tests/cpp_integration_tests/test_main_model_sc.cpp b/tests/cpp_integration_tests/test_main_model_sc.cpp deleted file mode 100644 index 59498c76d..000000000 --- a/tests/cpp_integration_tests/test_main_model_sc.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// SPDX-FileCopyrightText: Contributors to the Power Grid Model project -// -// SPDX-License-Identifier: MPL-2.0 - -#include -#include - -#include - -namespace power_grid_model { -namespace { -using CalculationMethod::iec60909; -using CalculationType::short_circuit; -using enum CalculationSymmetry; -} // namespace - -TEST_CASE("Test main model - short circuit") { - MainModel main_model{50.0, meta_data::meta_data_gen::meta_data}; - - SUBCASE("Single node + source") { // TODO(mgovers): existing validation case for this is too difficult. we may want - // to keep this. Also should not live here but in SC solver - double const u_rated = 10e3; - double const u_ref = 1.0; - double const sk = 100e6; - double const rx_ratio = 0.1; - double const z_ref_abs = u_rated * u_rated / sk; - double const x_ref = z_ref_abs / sqrt(rx_ratio * rx_ratio + 1.0); - double const r_ref = x_ref * rx_ratio; - DoubleComplex const z_ref{r_ref, x_ref}; - double const r_f = 0.1; - double const x_f = 0.1; - DoubleComplex const z_f{r_f, x_f}; - - main_model.add_component({{1, u_rated}}); - main_model.add_component({{2, 1, 1, u_ref, nan, sk, rx_ratio, nan}}); - - SUBCASE("three phase fault - maximum voltage scaling") { - ShortCircuitVoltageScaling const voltage_scaling = ShortCircuitVoltageScaling::maximum; - constexpr double voltage_scaling_c = 1.1; - main_model.add_component({{3, 1, FaultType::three_phase, FaultPhase::default_value, 1, r_f, x_f}}); - main_model.set_construction_complete(); - - double const u_source = u_rated * voltage_scaling_c / sqrt3; - DoubleComplex const i_f = u_source / (z_ref + z_f); - double const i_f_abs = cabs(i_f); - DoubleComplex const u_node = i_f * z_f; - double const u_node_abs = cabs(u_node); - double const u_node_abs_pu = u_node_abs / (u_rated / sqrt3); - - SUBCASE("Symmetric Calculation") { - auto const solver_output = main_model.calculate( - {.calculation_type = short_circuit, - .calculation_symmetry = symmetric, - .calculation_method = iec60909, - .short_circuit_voltage_scaling = voltage_scaling}); - - std::vector fault_output(1); - main_model.output_result(solver_output, fault_output); - - CHECK(fault_output[0].i_f(0) == doctest::Approx(i_f_abs)); - - std::vector node_output(1); - main_model.output_result(solver_output, node_output); - CHECK(node_output[0].u_pu(0) == doctest::Approx(u_node_abs_pu)); - } - - SUBCASE("Asymmetric Calculation") { - auto const solver_output = main_model.calculate( - {.calculation_type = short_circuit, - .calculation_symmetry = asymmetric, - .calculation_method = iec60909, - .short_circuit_voltage_scaling = voltage_scaling}); - - std::vector fault_output(1); - main_model.output_result(solver_output, fault_output); - CHECK(fault_output[0].i_f(0) == doctest::Approx(i_f_abs)); - - std::vector node_output(1); - main_model.output_result(solver_output, node_output); - CHECK(node_output[0].u_pu(0) == doctest::Approx(u_node_abs_pu)); - } - } - SUBCASE("three phase fault - minimum voltage scaling") { - ShortCircuitVoltageScaling const voltage_scaling = ShortCircuitVoltageScaling::minimum; - constexpr double voltage_scaling_c = 1.0; - main_model.add_component({{3, 1, FaultType::three_phase, FaultPhase::default_value, 1, r_f, x_f}}); - main_model.set_construction_complete(); - - double const u_source = u_rated * voltage_scaling_c / sqrt3; - DoubleComplex const i_f = u_source / (z_ref + z_f); - double const i_f_abs = cabs(i_f); - DoubleComplex const u_node = i_f * z_f; - double const u_node_abs = cabs(u_node); - double const u_node_abs_pu = u_node_abs / (u_rated / sqrt3); - - SUBCASE("Symmetric Calculation") { - auto const solver_output = main_model.calculate( - {.calculation_type = short_circuit, - .calculation_symmetry = symmetric, - .calculation_method = iec60909, - .short_circuit_voltage_scaling = voltage_scaling}); - - std::vector fault_output(1); - main_model.output_result(solver_output, fault_output); - - CHECK(fault_output[0].i_f(0) == doctest::Approx(i_f_abs)); - - std::vector node_output(1); - main_model.output_result(solver_output, node_output); - CHECK(node_output[0].u_pu(0) == doctest::Approx(u_node_abs_pu)); - } - - SUBCASE("Asymmetric Calculation") { - auto const solver_output = main_model.calculate( - {.calculation_type = short_circuit, - .calculation_symmetry = asymmetric, - .calculation_method = iec60909, - .short_circuit_voltage_scaling = voltage_scaling}); - - std::vector fault_output(1); - main_model.output_result(solver_output, fault_output); - CHECK(fault_output[0].i_f(0) == doctest::Approx(i_f_abs)); - - std::vector node_output(1); - main_model.output_result(solver_output, node_output); - CHECK(node_output[0].u_pu(0) == doctest::Approx(u_node_abs_pu)); - } - } - } - - SUBCASE("Two nodes + branch + source") { // TODO(mgovers): existing validation case for this is too difficult. we - // may want to keep this - ShortCircuitVoltageScaling const voltage_scaling = ShortCircuitVoltageScaling::maximum; - constexpr double voltage_scaling_c = 1.1; - - main_model.add_component({{1, 10e4}, {2, 10e4}}); - main_model.add_component({{3, 1, 2, 1, 1, 10.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 1e3}}); - main_model.add_component({{4, 1, 1, 1.0, nan, nan, nan, nan}}); - - SUBCASE("single phase to ground fault") { - main_model.add_component( - {{5, 2, FaultType::single_phase_to_ground, FaultPhase::default_value, 1, nan, nan}}); - main_model.set_construction_complete(); - - auto const solver_output = - main_model.calculate({.calculation_type = short_circuit, - .calculation_symmetry = asymmetric, - .calculation_method = iec60909, - .short_circuit_voltage_scaling = voltage_scaling}); - - std::vector fault_output(1); - main_model.output_result(solver_output, fault_output); - CHECK(fault_output[0].i_f(0) == doctest::Approx(voltage_scaling_c * 10e4 / sqrt3)); - - std::vector node_output(2); - main_model.output_result(solver_output, node_output); - CHECK(node_output[0].u_pu(0) != doctest::Approx(voltage_scaling_c)); // influenced by fault - CHECK(node_output[1].u_pu(0) == doctest::Approx(0.0)); // fault location - - CHECK(node_output[0].u_pu(1) == doctest::Approx(voltage_scaling_c)); - CHECK(node_output[0].u_pu(2) == doctest::Approx(voltage_scaling_c)); - CHECK(node_output[1].u_pu(1) == doctest::Approx(voltage_scaling_c)); - CHECK(node_output[1].u_pu(2) == doctest::Approx(voltage_scaling_c)); - } - } -} - -TEST_CASE("Test main model - short circuit - Dataset input") { // TODO(mgovers): same logic as above but different input - // type. maybe make a validation case for this; otherwise - // may be removed - SUBCASE("Two nodes + branch + source") { - std::vector node_input{{1, 10e4}, {2, 10e4}}; - std::vector line_input{{3, 1, 2, 1, 1, 10.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 1e3}}; - std::vector source_input{{4, 1, 1, 1.0, nan, nan, nan, nan}}; - std::vector fault_input{ - {5, 2, FaultType::single_phase_to_ground, FaultPhase::default_value, 1, nan, nan}}; - - ConstDataset input_data{false, 1, "input", meta_data::meta_data_gen::meta_data}; - input_data.add_buffer("node", node_input.size(), node_input.size(), nullptr, node_input.data()); - input_data.add_buffer("line", line_input.size(), line_input.size(), nullptr, line_input.data()); - input_data.add_buffer("source", source_input.size(), source_input.size(), nullptr, source_input.data()); - input_data.add_buffer("fault", fault_input.size(), fault_input.size(), nullptr, fault_input.data()); - - MainModel model{50.0, input_data}; - - std::vector node_output(2); - - MutableDataset result_data{false, 1, "sc_output", meta_data::meta_data_gen::meta_data}; - result_data.add_buffer("node", node_output.size(), node_output.size(), nullptr, node_output.data()); - - model.calculate({.calculation_type = short_circuit, - .calculation_symmetry = asymmetric, - .calculation_method = iec60909, - .short_circuit_voltage_scaling = ShortCircuitVoltageScaling::maximum}, - result_data); - - CHECK(node_output[0].u_pu(0) != doctest::Approx(1.0)); // influenced by fault - CHECK(node_output[1].u_pu(0) == doctest::Approx(0.0)); // fault location - } -} - -} // namespace power_grid_model diff --git a/tests/data/power_flow/dummy-test-batch-incomplete-input/asym_output_batch.json b/tests/data/power_flow/dummy-test-batch-incomplete-input/asym_output_batch.json new file mode 100644 index 000000000..92f465128 --- /dev/null +++ b/tests/data/power_flow/dummy-test-batch-incomplete-input/asym_output_batch.json @@ -0,0 +1,44 @@ +{ + "version": "1.0", + "type": "asym_output", + "is_batch": true, + "attributes": {}, + "data": [ + { + "node": [ + { + "id": 1, + "u": [4.618802153517006, 4.618802153517006, 4.618802153517006] + }, + { + "id": 2, + "u": [3.464101615137755, 3.464101615137755, 3.464101615137755] + } + ] + }, + { + "node": [ + { + "id": 1, + "u": [4.618802153517006, 4.618802153517006, 4.618802153517006] + }, + { + "id": 2, + "u": [3.464101615137755, 3.464101615137755, 3.464101615137755] + } + ] + }, + { + "node": [ + { + "id": 1, + "u": [5.196152422706632, 5.196152422706632, 5.196152422706632] + }, + { + "id": 2, + "u": [4.618802153517006, 4.618802153517006, 4.618802153517006] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/data/power_flow/dummy-test-batch-incomplete-input/asym_output_batch.json.license b/tests/data/power_flow/dummy-test-batch-incomplete-input/asym_output_batch.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/power_flow/dummy-test-batch-incomplete-input/asym_output_batch.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/power_flow/dummy-test-batch-incomplete-input/input.json b/tests/data/power_flow/dummy-test-batch-incomplete-input/input.json index caebcd92c..74b29a72f 100644 --- a/tests/data/power_flow/dummy-test-batch-incomplete-input/input.json +++ b/tests/data/power_flow/dummy-test-batch-incomplete-input/input.json @@ -25,7 +25,11 @@ "x1": 1.0, "c1": 0.0, "tan1": 0.0, - "i_n": 1e3 + "i_n": 1e3, + "r0": 0.0, + "x0": 1.0, + "c0": 0.0, + "tan0": 0.0 } ], "source": [ diff --git a/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/input.json b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/input.json new file mode 100644 index 000000000..77d7f4f9f --- /dev/null +++ b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/input.json @@ -0,0 +1,52 @@ +{ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + { + "id": 1, + "u_rated": 100000.0 + }, + { + "id": 2, + "u_rated": 100000.0 + } + ], + "line": [ + { + "id": 3, + "from_node": 1, + "to_node": 2, + "from_status": 1, + "to_status": 1, + "r1": 10.0, + "x1": 0.0, + "c1": 0.0, + "tan1": 0.0, + "r0": 10.0, + "x0": 0.0, + "c0": 0.0, + "tan0": 0.0, + "i_n": 1e3 + } + ], + "source": [ + { + "id": 4, + "node": 1, + "status": 1, + "u_ref": 1.0 + } + ], + "fault": [ + { + "id": 5, + "status": 1, + "fault_type": 1, + "fault_object": 1 + } + ] + } +} \ No newline at end of file diff --git a/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/input.json.license b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/input.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/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/short_circuit/branch_source_single_phase_to_ground_c_maximum/params.json b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/params.json new file mode 100644 index 000000000..71db2d5f8 --- /dev/null +++ b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/params.json @@ -0,0 +1,6 @@ +{ + "calculation_method": "iec60909", + "rtol": 1e-08, + "atol": 1e-08, + "short_circuit_voltage_scaling": "maximum" +} \ No newline at end of file diff --git a/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/params.json.license b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/params.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/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/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output.json b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output.json new file mode 100644 index 000000000..5d9bd8e38 --- /dev/null +++ b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output.json @@ -0,0 +1,39 @@ +{ + "version": "1.0", + "type": "sc_output", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + { + "id": 1, + "energized": 1, + "u_pu": [ + 0.0, + 1.1, + 1.1 + ] + }, + { + "id": 2, + "energized": 1, + "u_pu": [ + 0.0, + 1.1, + 1.1 + ] + } + ], + "fault": [ + { + "id": 5, + "energized": 1, + "i_f": [ + 63508.52961085883410, + 0.0, + 0.0 + ] + } + ] + } +} \ No newline at end of file diff --git a/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output.json.license b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output.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/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output_batch.json b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output_batch.json new file mode 100644 index 000000000..f28c4e4b8 --- /dev/null +++ b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output_batch.json @@ -0,0 +1,74 @@ +{ + "version": "1.0", + "type": "sc_output", + "is_batch": true, + "attributes": {}, + "data": [ + { + "node": [ + { + "id": 1, + "energized": 1, + "u_pu": [ + 0.0, + 1.1, + 1.1 + ] + }, + { + "id": 2, + "energized": 1, + "u_pu": [ + 0.0, + 1.1, + 1.1 + ] + } + ], + "fault": [ + { + "id": 5, + "energized": 1, + "i_f": [ + 63508.52961085883410, + 0.0, + 0.0 + ] + } + ] + }, + { + "node": [ + { + "id": 1, + "energized": 1, + "u_pu": [ + 1.083914433075477, + 1.1, + 1.1 + ] + }, + { + "id": 2, + "energized": 1, + "u_pu": [ + 0.0, + 1.1, + 1.1 + ] + } + ], + "fault": [ + { + "id": 5, + "energized": 1, + "i_f": [ + 6257.9828971464731, + 0.0, + 0.0 + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output_batch.json.license b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output_batch.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/sc_output_batch.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/short_circuit/branch_source_single_phase_to_ground_c_maximum/update_batch.json b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/update_batch.json new file mode 100644 index 000000000..dffeab919 --- /dev/null +++ b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/update_batch.json @@ -0,0 +1,24 @@ +{ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "fault": [ + { + "id": 5, + "fault_object": 1 + } + ] + }, + { + "fault": [ + { + "id": 5, + "fault_object": 2 + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/update_batch.json.license b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/update_batch.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/short_circuit/branch_source_single_phase_to_ground_c_maximum/update_batch.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/short_circuit/single_node_source_three_phase_c_maximum/input.json b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/input.json new file mode 100644 index 000000000..ee938e6e0 --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/input.json @@ -0,0 +1,20 @@ +{ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": + [ + {"id": 1, "u_rated": 10000.0} + ], + "source": + [ + {"id": 2, "node": 1, "status": 1, "u_ref": 1.0, "sk": 1e8, "rx_ratio": 0.1} + ], + "fault": + [ + {"id": 3, "status": 1, "fault_type": 0, "fault_phase": -1, "fault_object": 1, "r_f": 0.1, "x_f": 0.1} + ] + } +} \ No newline at end of file diff --git a/tests/data/short_circuit/single_node_source_three_phase_c_maximum/input.json.license b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/input.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/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/short_circuit/single_node_source_three_phase_c_maximum/params.json b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/params.json new file mode 100644 index 000000000..71db2d5f8 --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/params.json @@ -0,0 +1,6 @@ +{ + "calculation_method": "iec60909", + "rtol": 1e-08, + "atol": 1e-08, + "short_circuit_voltage_scaling": "maximum" +} \ No newline at end of file diff --git a/tests/data/short_circuit/single_node_source_three_phase_c_maximum/params.json.license b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/params.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/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/short_circuit/single_node_source_three_phase_c_maximum/sc_output.json b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/sc_output.json new file mode 100644 index 000000000..b2be998d7 --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/sc_output.json @@ -0,0 +1,30 @@ +{ + "version": "1.0", + "type": "sc_output", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + { + "id": 1, + "energized": 1, + "u_pu": [ + 0.13976168322743643, + 0.13976168322743643, + 0.13976168322743643 + ] + } + ], + "fault": [ + { + "id": 3, + "energized": 1, + "i_f": [ + 5705.7468249952872, + 5705.7468249952872, + 5705.7468249952872 + ] + } + ] + } +} \ No newline at end of file diff --git a/tests/data/short_circuit/single_node_source_three_phase_c_maximum/sc_output.json.license b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/sc_output.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_maximum/sc_output.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/short_circuit/single_node_source_three_phase_c_minimum/input.json b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/input.json new file mode 100644 index 000000000..ee938e6e0 --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/input.json @@ -0,0 +1,20 @@ +{ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": + [ + {"id": 1, "u_rated": 10000.0} + ], + "source": + [ + {"id": 2, "node": 1, "status": 1, "u_ref": 1.0, "sk": 1e8, "rx_ratio": 0.1} + ], + "fault": + [ + {"id": 3, "status": 1, "fault_type": 0, "fault_phase": -1, "fault_object": 1, "r_f": 0.1, "x_f": 0.1} + ] + } +} \ No newline at end of file diff --git a/tests/data/short_circuit/single_node_source_three_phase_c_minimum/input.json.license b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/input.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/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/short_circuit/single_node_source_three_phase_c_minimum/params.json b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/params.json new file mode 100644 index 000000000..fe50f74cf --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/params.json @@ -0,0 +1,6 @@ +{ + "calculation_method": "iec60909", + "rtol": 1e-08, + "atol": 1e-08, + "short_circuit_voltage_scaling": "minimum" +} \ No newline at end of file diff --git a/tests/data/short_circuit/single_node_source_three_phase_c_minimum/params.json.license b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/params.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/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/short_circuit/single_node_source_three_phase_c_minimum/sc_output.json b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/sc_output.json new file mode 100644 index 000000000..41009b83d --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/sc_output.json @@ -0,0 +1,30 @@ +{ + "version": "1.0", + "type": "sc_output", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + { + "id": 1, + "energized": 1, + "u_pu": [ + 0.12705607566130581, + 0.12705607566130581, + 0.12705607566130581 + ] + } + ], + "fault": [ + { + "id": 3, + "energized": 1, + "i_f": [ + 5187.0425681775332, + 5187.0425681775332, + 5187.0425681775332 + ] + } + ] + } +} \ No newline at end of file diff --git a/tests/data/short_circuit/single_node_source_three_phase_c_minimum/sc_output.json.license b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/sc_output.json.license new file mode 100644 index 000000000..760105916 --- /dev/null +++ b/tests/data/short_circuit/single_node_source_three_phase_c_minimum/sc_output.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/native_api_tests/test_api_model.cpp b/tests/native_api_tests/test_api_model.cpp index e828e1069..e7cd9dfa6 100644 --- a/tests/native_api_tests/test_api_model.cpp +++ b/tests/native_api_tests/test_api_model.cpp @@ -230,8 +230,11 @@ TEST_CASE("API Model") { Model model_dummy{std::move(model)}; model = std::move(model_dummy); } + SUBCASE("Test Copyability") { + Model const model_dummy{std::move(model)}; + model = Model{model_dummy}; + } SUBCASE("Single power flow") { - // Common checker used for all single power flow test subcases auto check_common_node_results = [&]() { node_output.get_value(PGM_def_sym_output_node_id, node_result_id.data(), -1);