Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/tests for restore bug #467

Merged
merged 5 commits into from
Jan 15, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
218 changes: 170 additions & 48 deletions tests/cpp_unit_tests/test_main_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1089,37 +1089,44 @@ TEST_CASE("Test main model - runtime dispatch") {
}
}

TEST_CASE("Test main model - incomplete input but complete dataset") {
namespace {
auto incomplete_input_model(State const& state) -> MainModel {
MainModel main_model{50.0};

std::vector<SourceInput> const incomplete_source_input{{6, 1, 1, nan, nan, 1e12, nan, nan},
{10, 3, 1, nan, nan, 1e12, nan, nan}};
std::vector<SymLoadGenInput> incomplete_sym_load_input{{7, 3, 1, LoadGenType::const_y, nan, 0.0}};
std::vector<AsymLoadGenInput> incomplete_asym_load_input{
{8, 3, 1, LoadGenType::const_y, RealValue<false>{nan}, RealValue<false>{0.0}}};

ConstDataset input_data;
main_model.add_component<Node>(state.node_input);
main_model.add_component<Line>(state.line_input);
main_model.add_component<Link>(state.link_input);
main_model.add_component<Source>(incomplete_source_input);
main_model.add_component<SymLoad>(incomplete_sym_load_input);
main_model.add_component<AsymLoad>(incomplete_asym_load_input);
main_model.add_component<Shunt>(state.shunt_input);
main_model.set_construction_complete();

return main_model;
}
} // namespace

TEST_CASE("Test main model - incomplete input") {
using CalculationMethod::iterative_current;
using CalculationMethod::linear;
using CalculationMethod::linear_current;
using CalculationMethod::newton_raphson;

State state;
auto main_model = default_model(state);

std::vector<SourceInput> const incomplete_source_input{{6, 1, 1, nan, nan, 1e12, nan, nan},
{10, 3, 1, nan, nan, 1e12, nan, nan}};
std::vector<SymLoadGenInput> incomplete_sym_load_input{{7, 3, 1, LoadGenType::const_y, nan, nan}};
std::vector<AsymLoadGenInput> incomplete_asym_load_input{
{8, 3, 1, LoadGenType::const_y, RealValue<false>{nan}, RealValue<false>{nan}}};

ConstDataset input_data;
input_data["node"] = DataPointer<true>{state.node_input.data(), static_cast<Idx>(state.node_input.size())};
input_data["line"] = DataPointer<true>{state.line_input.data(), static_cast<Idx>(state.line_input.size())};
input_data["link"] = DataPointer<true>{state.link_input.data(), static_cast<Idx>(state.link_input.size())};
input_data["source"] =
DataPointer<true>{incomplete_source_input.data(), static_cast<Idx>(incomplete_source_input.size())};
input_data["sym_load"] =
DataPointer<true>{incomplete_sym_load_input.data(), static_cast<Idx>(incomplete_sym_load_input.size())};
input_data["asym_load"] =
DataPointer<true>{incomplete_asym_load_input.data(), static_cast<Idx>(incomplete_asym_load_input.size())};
input_data["shunt"] = DataPointer<true>{state.shunt_input.data(), static_cast<Idx>(state.shunt_input.size())};
auto test_model = incomplete_input_model(state);

std::vector<SourceUpdate> complete_source_update{{6, 1, 1.05, nan}, {10, 1, 1.05, 0}};
std::vector<SymLoadGenUpdate> complete_sym_load_update{{7, 1, 0.5e6, 0.0}};
std::vector<SymLoadGenUpdate> complete_sym_load_update{{7, 1, 0.5e6, nan}};
std::vector<AsymLoadGenUpdate> complete_asym_load_update{
{8, 1, RealValue<false>{0.5e6 / 3.0}, RealValue<false>{0.0}}};
{8, 1, RealValue<false>{0.5e6 / 3.0}, RealValue<false>{nan}}};

ConstDataset update_data;
update_data["source"] =
Expand All @@ -1129,13 +1136,25 @@ TEST_CASE("Test main model - incomplete input but complete dataset") {
update_data["asym_load"] =
DataPointer<true>{complete_asym_load_update.data(), static_cast<Idx>(complete_asym_load_update.size())};

MainModel test_model{50.0, input_data};
std::vector<SourceUpdate> incomplete_source_update{{6, na_IntS, nan, nan}, {10, na_IntS, nan, nan}};
std::vector<SymLoadGenUpdate> incomplete_sym_load_update{{7, na_IntS, nan, nan}};
std::vector<AsymLoadGenUpdate> incomplete_asym_load_update{
{8, na_IntS, RealValue<false>{nan}, RealValue<false>{nan}}};

ConstDataset incomplete_update_data;
incomplete_update_data["source"] =
DataPointer<true>{incomplete_source_update.data(), static_cast<Idx>(incomplete_source_update.size())};
incomplete_update_data["sym_load"] =
DataPointer<true>{incomplete_sym_load_update.data(), static_cast<Idx>(incomplete_sym_load_update.size())};
incomplete_update_data["asym_load"] =
DataPointer<true>{incomplete_asym_load_update.data(), static_cast<Idx>(incomplete_asym_load_update.size())};

MainModel const ref_model{main_model};

Dataset test_result_data;
Dataset ref_result_data;

SUBCASE("Symmetrical") {
SUBCASE("Symmetrical - Complete") {
std::vector<NodeOutput<true>> test_sym_node(state.sym_node.size());
std::vector<NodeOutput<true>> ref_sym_node(state.sym_node.size());
test_result_data["node"] = DataPointer<false>{test_sym_node.data(), static_cast<Idx>(test_sym_node.size())};
Expand Down Expand Up @@ -1166,7 +1185,7 @@ TEST_CASE("Test main model - incomplete input but complete dataset") {
CHECK(test_sym_node[2].u_pu == doctest::Approx(ref_sym_node[2].u_pu));
}

SUBCASE("Asymmetrical") {
SUBCASE("Asymmetrical - Complete") {
std::vector<NodeOutput<false>> test_asym_node(state.asym_node.size());
std::vector<NodeOutput<false>> ref_asym_node(state.asym_node.size());
test_result_data["node"] = DataPointer<false>{test_asym_node.data(), static_cast<Idx>(test_asym_node.size())};
Expand All @@ -1192,43 +1211,146 @@ TEST_CASE("Test main model - incomplete input but complete dataset") {
main_model.calculate_power_flow<false>(1e-8, 20, newton_raphson, ref_result_data, update_data, -1);
}

CHECK(test_asym_node[0].u_pu(0) == doctest::Approx(ref_asym_node[0].u_pu(0)));
CHECK(test_asym_node[0].u_pu(1) == doctest::Approx(ref_asym_node[0].u_pu(1)));
CHECK(test_asym_node[0].u_pu(2) == doctest::Approx(ref_asym_node[0].u_pu(2)));
CHECK(test_asym_node[1].u_pu(0) == doctest::Approx(ref_asym_node[1].u_pu(0)));
CHECK(test_asym_node[1].u_pu(1) == doctest::Approx(ref_asym_node[1].u_pu(1)));
CHECK(test_asym_node[1].u_pu(2) == doctest::Approx(ref_asym_node[1].u_pu(2)));
CHECK(test_asym_node[2].u_pu(0) == doctest::Approx(ref_asym_node[2].u_pu(0)));
CHECK(test_asym_node[2].u_pu(1) == doctest::Approx(ref_asym_node[2].u_pu(1)));
CHECK(test_asym_node[2].u_pu(2) == doctest::Approx(ref_asym_node[2].u_pu(2)));
for (auto component_idx : {0, 1, 2}) {
CAPTURE(component_idx);

for (auto phase_idx : {0, 1, 2}) {
CAPTURE(phase_idx);

CHECK(test_asym_node[component_idx].u_pu(phase_idx) ==
doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx)));
}
}
}

SUBCASE("Symmetrical - Incomplete") {
update_data = {};

std::vector<NodeOutput<true>> test_sym_node(state.sym_node.size());
std::vector<NodeOutput<true>> ref_sym_node(state.sym_node.size());
test_result_data["node"] = DataPointer<false>{test_sym_node.data(), static_cast<Idx>(test_sym_node.size())};
ref_result_data["node"] = DataPointer<false>{ref_sym_node.data(), static_cast<Idx>(ref_sym_node.size())};

CHECK_THROWS_AS(test_model.calculate_power_flow<true>(1e-8, 1, linear), SparseMatrixError);
CHECK_THROWS_AS(test_model.calculate_power_flow<true>(1e-8, 1, linear, test_result_data), SparseMatrixError);
CHECK_THROWS_AS(test_model.calculate_power_flow<true>(1e-8, 1, linear, test_result_data, update_data),
SparseMatrixError);
SUBCASE("Direct call") {
CHECK_THROWS_AS(test_model.calculate_power_flow<true>(1e-8, 1, linear), SparseMatrixError);
}
SUBCASE("Target dataset") {
CHECK_THROWS_AS(test_model.calculate_power_flow<true>(1e-8, 1, linear, test_result_data),
SparseMatrixError);
}
SUBCASE("Empty update dataset") {
update_data = {};

CHECK_THROWS_AS(test_model.calculate_power_flow<true>(1e-8, 1, linear, test_result_data, update_data),
SparseMatrixError);
}
SUBCASE("Update dataset") {
CHECK_THROWS_AS(
test_model.calculate_power_flow<true>(1e-8, 1, linear, test_result_data, incomplete_update_data),
BatchCalculationError);
}
}

SUBCASE("Asymmetrical - Incomplete") {
update_data = {};

std::vector<NodeOutput<false>> test_sym_node(state.sym_node.size());
std::vector<NodeOutput<false>> ref_sym_node(state.sym_node.size());
test_result_data["node"] = DataPointer<false>{test_sym_node.data(), static_cast<Idx>(test_sym_node.size())};

SUBCASE("Direct call") {
CHECK_THROWS_AS(test_model.calculate_power_flow<false>(1e-8, 1, linear), SparseMatrixError);
}
SUBCASE("Target dataset") {
CHECK_THROWS_AS(test_model.calculate_power_flow<false>(1e-8, 1, linear, test_result_data),
SparseMatrixError);
}
SUBCASE("Empty update dataset") {
update_data = {};

CHECK_THROWS_AS(test_model.calculate_power_flow<false>(1e-8, 1, linear, test_result_data, update_data),
SparseMatrixError);
}
SUBCASE("Update dataset") {
CHECK_THROWS_AS(
test_model.calculate_power_flow<false>(1e-8, 1, linear, test_result_data, incomplete_update_data),
BatchCalculationError);
}
}
}

TEST_CASE("Incomplete followed by complete") {
using CalculationMethod::linear;

State state;
auto main_model = default_model(state);
auto test_model = incomplete_input_model(state);

constexpr Idx batch_size = 2;

std::vector<SourceUpdate> mixed_source_update{
{6, 1, nan, nan}, {10, 1, nan, nan}, {6, 1, 1.05, nan}, {10, 1, 1.05, 0}};
std::vector<SymLoadGenUpdate> mixed_sym_load_update{{7, 1, nan, 1.0}, {7, 1, 0.5e6, nan}};
std::vector<AsymLoadGenUpdate> mixed_asym_load_update{{8, 1, RealValue<false>{nan}, RealValue<false>{1.0}},
{8, 1, RealValue<false>{0.5e6 / 3.0}, RealValue<false>{nan}}};

auto const source_indptr = IdxVector{0, 0, static_cast<Idx>(mixed_source_update.size())};

REQUIRE(source_indptr.size() == batch_size + 1);

ConstDataset mixed_update_data;
mixed_update_data["source"] = DataPointer<true>{mixed_source_update.data(), batch_size, 2};
mixed_update_data["sym_load"] = DataPointer<true>{mixed_sym_load_update.data(), batch_size, 1};
mixed_update_data["asym_load"] = DataPointer<true>{mixed_asym_load_update.data(), batch_size, 1};

ConstDataset second_scenario_update_data;
second_scenario_update_data["source"] = DataPointer<true>{mixed_source_update.data() + 2, 2};
second_scenario_update_data["sym_load"] = DataPointer<true>{mixed_sym_load_update.data() + 1, 1};
second_scenario_update_data["asym_load"] = DataPointer<true>{mixed_asym_load_update.data() + 1, 1};

Dataset test_result_data;
Dataset ref_result_data;

SUBCASE("Symmetrical") {
std::vector<NodeOutput<true>> test_sym_node(batch_size * state.sym_node.size(),
{na_IntID, na_IntS, nan, nan, nan, nan, nan});
std::vector<NodeOutput<true>> ref_sym_node(state.sym_node.size(), {na_IntID, na_IntS, nan, nan, nan, nan, nan});
test_result_data["node"] =
DataPointer<false>{test_sym_node.data(), batch_size, static_cast<Idx>(state.sym_node.size())};
ref_result_data["node"] = DataPointer<false>{ref_sym_node.data(), static_cast<Idx>(ref_sym_node.size())};

CHECK_THROWS_AS(test_model.calculate_power_flow<false>(1e-8, 1, linear), SparseMatrixError);
CHECK_THROWS_AS(test_model.calculate_power_flow<false>(1e-8, 1, linear, test_result_data), SparseMatrixError);
CHECK_THROWS_AS(test_model.calculate_power_flow<false>(1e-8, 1, linear, test_result_data, update_data),
SparseMatrixError);
CHECK_THROWS_AS(test_model.calculate_power_flow<true>(1e-8, 1, linear, test_result_data, mixed_update_data),
BatchCalculationError);
main_model.calculate_power_flow<true>(1e-8, 1, linear, ref_result_data, second_scenario_update_data, -1);

CHECK(is_nan(test_sym_node[0].u_pu));
CHECK(is_nan(test_sym_node[1].u_pu));
CHECK(is_nan(test_sym_node[2].u_pu));
CHECK(test_sym_node[state.sym_node.size() + 0].u_pu == doctest::Approx(ref_sym_node[0].u_pu));
CHECK(test_sym_node[state.sym_node.size() + 1].u_pu == doctest::Approx(ref_sym_node[1].u_pu));
CHECK(test_sym_node[state.sym_node.size() + 2].u_pu == doctest::Approx(ref_sym_node[2].u_pu));
}

SUBCASE("Asymmetrical") {
std::vector<NodeOutput<false>> test_asym_node(
batch_size * state.sym_node.size(), {na_IntID, na_IntS, RealValue<false>{nan}, RealValue<false>{nan},
RealValue<false>{nan}, RealValue<false>{nan}, RealValue<false>{nan}});
std::vector<NodeOutput<false>> ref_asym_node(
state.sym_node.size(), {na_IntID, na_IntS, RealValue<false>{nan}, RealValue<false>{nan},
RealValue<false>{nan}, RealValue<false>{nan}, RealValue<false>{nan}});
test_result_data["node"] =
DataPointer<false>{test_asym_node.data(), batch_size, static_cast<Idx>(state.sym_node.size())};
ref_result_data["node"] = DataPointer<false>{ref_asym_node.data(), static_cast<Idx>(ref_asym_node.size())};

CHECK_THROWS_AS(test_model.calculate_power_flow<false>(1e-8, 1, linear, test_result_data, mixed_update_data),
BatchCalculationError);
main_model.calculate_power_flow<false>(1e-8, 1, linear, ref_result_data, second_scenario_update_data, -1);

for (auto component_idx : {0, 1, 2}) {
CAPTURE(component_idx);

CHECK(is_nan(test_asym_node[component_idx].u_pu));

for (auto phase_idx : {0, 1, 2}) {
CAPTURE(phase_idx);

CHECK(test_asym_node[state.asym_node.size() + component_idx].u_pu(phase_idx) ==
doctest::Approx(ref_asym_node[component_idx].u_pu(phase_idx)));
}
}
}
}

Expand Down