From 833427ed562a85764c28bc31d7c2973d49410df9 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Fri, 22 Mar 2024 12:57:39 +0100 Subject: [PATCH 01/14] setup for python errors Signed-off-by: Martijn Govers --- .../power_grid_model/common/exception.hpp | 6 +- src/power_grid_model/core/error_handling.py | 87 +++++++++++++++++-- src/power_grid_model/errors.py | 79 +++++++++++++++-- 3 files changed, 154 insertions(+), 18 deletions(-) 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 edd6d7b13f..b52a66eddd 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 @@ -57,7 +57,7 @@ class InvalidBranch3 : public PowerGridError { class InvalidTransformerClock : public PowerGridError { public: InvalidTransformerClock(ID id, IntS clock) { - append_msg("Invalid clock for transformer " + std::to_string(id) + ", clock " + std::to_string(clock) + '\n'); + append_msg("Invalid clock for transformer " + std::to_string(id) + ", clock " + std::to_string(clock) + '\n'); } }; @@ -140,7 +140,7 @@ class InvalidCalculationMethod : public CalculationError { class UnknownAttributeName : public PowerGridError { public: explicit UnknownAttributeName(std::string const& attr_name) { - append_msg("Unknown attribute name!" + attr_name + "\n"); + append_msg("Unknown attribute name! " + attr_name + "\n"); } }; @@ -168,7 +168,7 @@ class InvalidShortCircuitPhases : public PowerGridError { class InvalidShortCircuitPhaseOrType : public PowerGridError { public: InvalidShortCircuitPhaseOrType() { - append_msg("During one calculation the short circuit types phases should be similar for all faults \n"); + append_msg("During one calculation the short circuit types phases should be similar for all faults\n"); } }; diff --git a/src/power_grid_model/core/error_handling.py b/src/power_grid_model/core/error_handling.py index 705635134a..f7ad112788 100644 --- a/src/power_grid_model/core/error_handling.py +++ b/src/power_grid_model/core/error_handling.py @@ -6,13 +6,33 @@ Error handling """ +import re from typing import Optional import numpy as np from power_grid_model.core.index_integer import IdxNp from power_grid_model.core.power_grid_core import power_grid_core as pgc -from power_grid_model.errors import PowerGridBatchError, PowerGridError, PowerGridSerializationError +from power_grid_model.errors import ( + ConflictID, + ConflictVoltage, + IDNotFound, + IDWrongType, + InvalidBranch, + InvalidBranch3, + InvalidCalculationMethod, + InvalidMeasuredObject, + InvalidShortCircuitPhaseOrType, + InvalidTransformerClock, + IterationDiverge, + MissingCaseForEnumError, + NotObservableError, + PowerGridBatchError, + PowerGridError, + PowerGridSerializationError, + SparseMatrixError, + UnknownAttributeName, +) VALIDATOR_MSG = "\nTry validate_input_data() or validate_batch_data() to validate your data.\n" # error codes @@ -21,13 +41,64 @@ PGM_BATCH_ERROR = 2 PGM_SERIALIZATION_ERROR = 3 - -def find_error(batch_size: int = 1) -> Optional[RuntimeError]: +_MISSING_CASE_FOR_ENUM_RE = re.compile(r"(\w+) is not implemented for (\w+) #(\d+)!\n") +_CONFLICT_VOLTAGE_RE = re.compile( + r"Conflicting voltage for line (\d+)\n voltage at from node (\d+) is (.*)\n voltage at to node (\d+) is (.*)\n" +) +_INVALID_BRANCH_RE = re.compile(r"Branch (\d+) has the same from- and to-node (\d+),\n This is not allowed!\n") +_INVALID_BRANCH3_RE = re.compile( + r"Branch3 (\d+) is connected to the same node at least twice. Node 1/2/3: (\d+)/(\d+)/(\d+),\n" + r"This is not allowed!\n" +) +_INVALID_TRANSFORMER_CLOCK_RE = re.compile(r"Invalid clock for transformer (\d+), clock (\d+)\n") +_SPARSE_MATRIX_ERROR_RE = re.compile(r"Sparse matrix error") # multiple different flavors +_NOT_OBSERVABLE_ERROR_RE = re.compile(r"Not enough measurements available for state estimation.\n") +_ITERATION_DIVERGE_RE = re.compile( + r"Iteration failed to converge after (\d+) iterations! Max deviation: (.*), error tolerance: (.*).\n" +) +_CONFLICT_ID_RE = re.compile(r"Conflicting id detected: (\d+)\n") +_ID_NOT_FOUND_RE = re.compile(r"The id cannot be found: (\d+)\n") +_INVALID_MEASURED_OBJECT_RE = re.compile(r"(\w+) is not supported for (\w+)") +_ID_WRONG_TYPE_RE = re.compile(r"Wrong type for object with id (\d+)\n") +_INVALID_CALCULATION_METHOD_RE = re.compile(r"The calculation method is invalid for this calculation!") +_UNKNOWN_ATTRIBUTE_NAME_RE = re.compile(r"Unknown attribute name! (.*)\n") +_INVALID_SHORT_CIRCUIT_PHASE_OR_TYPE_RE = re.compile(r"short circuit type") # multiple different flavors + +_ERROR_MESSAGE_PATTERNS = { + _MISSING_CASE_FOR_ENUM_RE: MissingCaseForEnumError, + _CONFLICT_VOLTAGE_RE: ConflictVoltage, + _INVALID_BRANCH_RE: InvalidBranch, + _INVALID_BRANCH3_RE: InvalidBranch3, + _INVALID_TRANSFORMER_CLOCK_RE: InvalidTransformerClock, + _SPARSE_MATRIX_ERROR_RE: SparseMatrixError, + _NOT_OBSERVABLE_ERROR_RE: NotObservableError, + _ITERATION_DIVERGE_RE: IterationDiverge, + _CONFLICT_ID_RE: ConflictID, + _ID_NOT_FOUND_RE: IDNotFound, + _INVALID_MEASURED_OBJECT_RE: InvalidMeasuredObject, + _ID_WRONG_TYPE_RE: IDWrongType, + _INVALID_CALCULATION_METHOD_RE: InvalidCalculationMethod, + _UNKNOWN_ATTRIBUTE_NAME_RE: UnknownAttributeName, + _INVALID_SHORT_CIRCUIT_PHASE_OR_TYPE_RE: InvalidShortCircuitPhaseOrType, +} + + +def _interpret_error(message: str, decode_error: bool = True) -> PowerGridError: + if decode_error: + for pattern, type_ in _ERROR_MESSAGE_PATTERNS.items(): + if pattern.search(message) is not None: + return type_(message) + + return PowerGridError(message) + + +def find_error(batch_size: int = 1, decode_error: bool = True) -> Optional[RuntimeError]: """ Check if there is an error and return it Args: - batch_size: Size of batch + batch_size: (int, optional): Size of batch. Defaults to 1. + decode_error (bool, optional): Decode the error message(s) to derived error classes. Defaults to True Returns: error object, can be none @@ -47,6 +118,7 @@ def find_error(batch_size: int = 1) -> Optional[RuntimeError]: failed_msgptr = pgc.batch_errors() error.failed_scenarios = np.ctypeslib.as_array(failed_idxptr, shape=(n_fails,)).copy() error.error_messages = [failed_msgptr[i].decode() for i in range(n_fails)] # type: ignore + error.errors = [_interpret_error(message, decode_error=decode_error) for message in error.error_messages] all_scenarios = np.arange(batch_size, dtype=IdxNp) mask = np.ones(batch_size, dtype=np.bool_) mask[error.failed_scenarios] = False @@ -70,13 +142,16 @@ def assert_no_error(batch_size: int = 1): raise error -def handle_errors(continue_on_batch_error: bool, batch_size: int = 1) -> Optional[PowerGridBatchError]: +def handle_errors( + continue_on_batch_error: bool, batch_size: int = 1, decode_error: bool = True +) -> Optional[PowerGridBatchError]: """ Handle any errors in the way that is specified. Args: continue_on_batch_error (bool): Return the error when the error type is a batch error instead of reraising it. batch_size (int, optional): Size of batch. Defaults to 1. + decode_error (bool, optional): Decode the error message(s) to derived error classes. Defaults to True Raises: error: Any errors previously encountered, unless it was a batch error and continue_on_batch_error was True. @@ -85,7 +160,7 @@ def handle_errors(continue_on_batch_error: bool, batch_size: int = 1) -> Optiona Optional[PowerGridBatchError]: None if there were no errors, or the previously encountered error if it was a batch error and continue_on_batch_error was True. """ - error: Optional[RuntimeError] = find_error(batch_size=batch_size) + error: Optional[RuntimeError] = find_error(batch_size=batch_size, decode_error=decode_error) if error is None: return None diff --git a/src/power_grid_model/errors.py b/src/power_grid_model/errors.py index 1b51b633c5..4955f3f51a 100644 --- a/src/power_grid_model/errors.py +++ b/src/power_grid_model/errors.py @@ -12,22 +12,83 @@ class PowerGridError(RuntimeError): - """ - Generic power grid error - """ + """Generic power grid error""" class PowerGridBatchError(PowerGridError): - """ - Error occurs in batch calculation - """ + """Error occurs in batch calculation""" failed_scenarios: np.ndarray succeeded_scenarios: np.ndarray error_messages: List[str] + errors: List[PowerGridError] + + +class MissingCaseForEnumError(PowerGridError): + """An enum value is not covered in a for loop. + + This usually happens when an invalid combination of (enum) settings is provided.""" + + +class ConflictVoltage(PowerGridError): + """There is a confliciting voltage""" + + +class InvalidBranch(PowerGridError): + """A branch is invalid""" + + +class InvalidBranch3(PowerGridError): + """A branch3 is invalid""" + + +class InvalidTransformerClock(PowerGridError): + """Invalid transformer clock found""" + + +class SparseMatrixError(PowerGridError): + """Attempting to invert a non-invertible matrix""" + + +class NotObservableError(SparseMatrixError): + """Attempting to solve a non-observable system""" + + +class IterationDiverge(PowerGridError): + """Unable to iteratively converge to an optimum within the set number of iterations and precision""" + + +class InvalidID(PowerGridError): + """An ID is invalid""" + + +class ConflictID(InvalidID): + """Conflicting IDs found""" + + +class IDNotFound(InvalidID): + """A reference to a non-existent ID was provided""" + + +class InvalidMeasuredObject(InvalidID): + """A provided measured object is invalid""" + + +class IDWrongType(InvalidID): + """A referenced ID points to a component that cannot be referenced here""" + + +class InvalidCalculationMethod(PowerGridError): + """Invalid calculation method provided""" + + +class UnknownAttributeName(PowerGridError): + """Unknown attribute provided""" + + +class InvalidShortCircuitPhaseOrType(PowerGridError): + """Invalid (combination of) short circuit types and phase(s) provided""" class PowerGridSerializationError(PowerGridError): - """ - Error occurs during (de-)serialization - """ + """Error occurs during (de-)serialization""" From f387e34839173e2d33f3f7acfba92face04c8157 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 26 Mar 2024 08:31:10 +0100 Subject: [PATCH 02/14] add tests for error subtype handling Signed-off-by: Martijn Govers --- .../power_grid_model/common/exception.hpp | 7 - src/power_grid_model/core/error_handling.py | 34 ++-- src/power_grid_model/errors.py | 4 - tests/unit/test_error_handling.py | 177 +++++++++++++++++- 4 files changed, 193 insertions(+), 29 deletions(-) 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 b52a66eddd..4c5d346687 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 @@ -137,13 +137,6 @@ class InvalidCalculationMethod : public CalculationError { InvalidCalculationMethod() : CalculationError("The calculation method is invalid for this calculation!") {} }; -class UnknownAttributeName : public PowerGridError { - public: - explicit UnknownAttributeName(std::string const& attr_name) { - append_msg("Unknown attribute name! " + attr_name + "\n"); - } -}; - class InvalidShortCircuitType : public PowerGridError { public: explicit InvalidShortCircuitType(FaultType short_circuit_type) { diff --git a/src/power_grid_model/core/error_handling.py b/src/power_grid_model/core/error_handling.py index f7ad112788..bba89790bb 100644 --- a/src/power_grid_model/core/error_handling.py +++ b/src/power_grid_model/core/error_handling.py @@ -31,7 +31,6 @@ PowerGridError, PowerGridSerializationError, SparseMatrixError, - UnknownAttributeName, ) VALIDATOR_MSG = "\nTry validate_input_data() or validate_batch_data() to validate your data.\n" @@ -41,27 +40,27 @@ PGM_BATCH_ERROR = 2 PGM_SERIALIZATION_ERROR = 3 -_MISSING_CASE_FOR_ENUM_RE = re.compile(r"(\w+) is not implemented for (\w+) #(\d+)!\n") +_MISSING_CASE_FOR_ENUM_RE = re.compile(r"(\w+) is not implemented for (\w+) #(-?\d+)!\n") _CONFLICT_VOLTAGE_RE = re.compile( - r"Conflicting voltage for line (\d+)\n voltage at from node (\d+) is (.*)\n voltage at to node (\d+) is (.*)\n" + r"Conflicting voltage for line (-?\d+)\n voltage at from node (-?\d+) is (.*)\n" + r" voltage at to node (-?\d+) is (.*)\n" ) -_INVALID_BRANCH_RE = re.compile(r"Branch (\d+) has the same from- and to-node (\d+),\n This is not allowed!\n") +_INVALID_BRANCH_RE = re.compile(r"Branch (-?\d+) has the same from- and to-node (-?\d+),\n This is not allowed!\n") _INVALID_BRANCH3_RE = re.compile( - r"Branch3 (\d+) is connected to the same node at least twice. Node 1/2/3: (\d+)/(\d+)/(\d+),\n" - r"This is not allowed!\n" + r"Branch3 (-?\d+) is connected to the same node at least twice. Node 1\/2\/3: (-?\d+)\/(-?\d+)\/(-?\d+),\n" + r" This is not allowed!\n" ) -_INVALID_TRANSFORMER_CLOCK_RE = re.compile(r"Invalid clock for transformer (\d+), clock (\d+)\n") +_INVALID_TRANSFORMER_CLOCK_RE = re.compile(r"Invalid clock for transformer (-?\d+), clock (-?\d+)\n") _SPARSE_MATRIX_ERROR_RE = re.compile(r"Sparse matrix error") # multiple different flavors _NOT_OBSERVABLE_ERROR_RE = re.compile(r"Not enough measurements available for state estimation.\n") _ITERATION_DIVERGE_RE = re.compile( - r"Iteration failed to converge after (\d+) iterations! Max deviation: (.*), error tolerance: (.*).\n" + r"Iteration failed to converge after (-?\d+) iterations! Max deviation: (.*), error tolerance: (.*).\n" ) -_CONFLICT_ID_RE = re.compile(r"Conflicting id detected: (\d+)\n") -_ID_NOT_FOUND_RE = re.compile(r"The id cannot be found: (\d+)\n") +_CONFLICT_ID_RE = re.compile(r"Conflicting id detected: (-?\d+)\n") +_ID_NOT_FOUND_RE = re.compile(r"The id cannot be found: (-?\d+)\n") _INVALID_MEASURED_OBJECT_RE = re.compile(r"(\w+) is not supported for (\w+)") -_ID_WRONG_TYPE_RE = re.compile(r"Wrong type for object with id (\d+)\n") +_ID_WRONG_TYPE_RE = re.compile(r"Wrong type for object with id (-?\d+)\n") _INVALID_CALCULATION_METHOD_RE = re.compile(r"The calculation method is invalid for this calculation!") -_UNKNOWN_ATTRIBUTE_NAME_RE = re.compile(r"Unknown attribute name! (.*)\n") _INVALID_SHORT_CIRCUIT_PHASE_OR_TYPE_RE = re.compile(r"short circuit type") # multiple different flavors _ERROR_MESSAGE_PATTERNS = { @@ -78,7 +77,6 @@ _INVALID_MEASURED_OBJECT_RE: InvalidMeasuredObject, _ID_WRONG_TYPE_RE: IDWrongType, _INVALID_CALCULATION_METHOD_RE: InvalidCalculationMethod, - _UNKNOWN_ATTRIBUTE_NAME_RE: UnknownAttributeName, _INVALID_SHORT_CIRCUIT_PHASE_OR_TYPE_RE: InvalidShortCircuitPhaseOrType, } @@ -109,7 +107,7 @@ def find_error(batch_size: int = 1, decode_error: bool = True) -> Optional[Runti if error_code == PGM_REGULAR_ERROR: error_message = pgc.error_message() error_message += VALIDATOR_MSG - return PowerGridError(error_message) + return _interpret_error(error_message, decode_error=decode_error) if error_code == PGM_BATCH_ERROR: error_message = "There are errors in the batch calculation." + VALIDATOR_MSG error = PowerGridBatchError(error_message) @@ -129,15 +127,19 @@ def find_error(batch_size: int = 1, decode_error: bool = True) -> Optional[Runti return RuntimeError("Unknown error!") -def assert_no_error(batch_size: int = 1): +def assert_no_error(batch_size: int = 1, decode_error: bool = True): """ Assert there is no error in the last operation If there is an error, raise it + Args: + batch_size (int, optional): Size of batch. Defaults to 1. + decode_error (bool, optional): Decode the error message(s) to derived error classes. Defaults to True + Returns: """ - error = find_error(batch_size=batch_size) + error = find_error(batch_size=batch_size, decode_error=decode_error) if error is not None: raise error diff --git a/src/power_grid_model/errors.py b/src/power_grid_model/errors.py index 4955f3f51a..afddc735f6 100644 --- a/src/power_grid_model/errors.py +++ b/src/power_grid_model/errors.py @@ -82,10 +82,6 @@ class InvalidCalculationMethod(PowerGridError): """Invalid calculation method provided""" -class UnknownAttributeName(PowerGridError): - """Unknown attribute provided""" - - class InvalidShortCircuitPhaseOrType(PowerGridError): """Invalid (combination of) short circuit types and phase(s) provided""" diff --git a/tests/unit/test_error_handling.py b/tests/unit/test_error_handling.py index 49469ebd3d..8fa2b0c6e2 100644 --- a/tests/unit/test_error_handling.py +++ b/tests/unit/test_error_handling.py @@ -7,6 +7,19 @@ import pytest from power_grid_model import PowerGridModel +from power_grid_model.core.power_grid_meta import initialize_array +from power_grid_model.enum import CalculationMethod, LoadGenType, MeasuredTerminalType +from power_grid_model.errors import ( + ConflictID, + ConflictVoltage, + IDWrongType, + InvalidBranch, + InvalidBranch3, + InvalidCalculationMethod, + InvalidMeasuredObject, + InvalidTransformerClock, + NotObservableError, +) def test_empty_model(): @@ -23,5 +36,165 @@ def test_empty_model(): def test_unknown_component_types(): model = PowerGridModel(input_data={}) - with pytest.raises(KeyError, match=r"artificial_type") as e: - model.calculate_power_flow(output_component_types={"artificial_type"}) + with pytest.raises(KeyError, match=r"fake_type"): + model.calculate_power_flow(output_component_types={"fake_type"}) + + +def test_handle_conflict_voltage_error(): + node_input = initialize_array("input", "node", 2) + node_input["id"] = [0, 1] + node_input["u_rated"] = [10.0e3, 20.0e3] + + line_input = initialize_array("input", "line", 1) + line_input["id"] = [2] + line_input["from_node"] = [0] + line_input["to_node"] = [1] + + with pytest.raises(ConflictVoltage): + PowerGridModel(input_data={"node": node_input, "line": line_input}) + + +@pytest.mark.skip(reason="TODO") +def test_handle_missing_case_for_enum_error(): + pass + + +def test_handle_invalid_branch_error(): + node_input = initialize_array("input", "node", 1) + node_input["id"] = [0] + node_input["u_rated"] = [0.0] + + line_input = initialize_array("input", "line", 1) + line_input["id"] = [1] + line_input["from_node"] = [0] + line_input["to_node"] = [0] + + with pytest.raises(InvalidBranch): + PowerGridModel(input_data={"node": node_input, "line": line_input}) + + +def test_handle_invalid_branch3_error(): + node_input = initialize_array("input", "node", 1) + node_input["id"] = [0] + node_input["u_rated"] = [0.0] + + three_winding_transformer_input = initialize_array("input", "three_winding_transformer", 1) + three_winding_transformer_input["id"] = [1] + three_winding_transformer_input["node_1"] = [0] + three_winding_transformer_input["node_2"] = [0] + three_winding_transformer_input["node_3"] = [0] + + with pytest.raises(InvalidBranch3): + PowerGridModel(input_data={"node": node_input, "three_winding_transformer": three_winding_transformer_input}) + + +def test_handle_invalid_transformer_clock_error(): + node_input = initialize_array("input", "node", 2) + node_input["id"] = [0, 1] + node_input["u_rated"] = [0.0, 0.0] + + transformer_input = initialize_array("input", "transformer", 1) + transformer_input["id"] = [2] + transformer_input["from_node"] = [0] + transformer_input["to_node"] = [1] + transformer_input["clock"] = [-1] + + with pytest.raises(InvalidTransformerClock): + PowerGridModel(input_data={"node": node_input, "transformer": transformer_input}) + + +@pytest.mark.skip(reason="TODO") +def test_handle_sparse_matrix_error(): + pass + + +def test_handle_not_observable_error(): + node_input = initialize_array("input", "node", 1) + node_input["id"] = [0] + node_input["u_rated"] = [10.0e3] + + source_input = initialize_array("input", "source", 1) + source_input["id"] = [1] + source_input["node"] = [0] + source_input["status"] = [1] + + sym_load_input = initialize_array("input", "sym_load", 1) + sym_load_input["id"] = [2] + sym_load_input["node"] = [0] + sym_load_input["status"] = [1] + sym_load_input["type"] = [LoadGenType.const_power] + + model = PowerGridModel(input_data={"node": node_input, "source": source_input, "sym_load": sym_load_input}) + with pytest.raises(NotObservableError): + model.calculate_state_estimation(calculation_method=CalculationMethod.iterative_linear) + + +@pytest.mark.skip(reason="TODO") +def test_handle_iteration_diverge_error(): + pass + + +def test_handle_conflict_id_error(): + node_input = initialize_array("input", "node", 2) + node_input["id"] = [0, 0] + node_input["u_rated"] = [0.0, 0.0] + + with pytest.raises(ConflictID): + PowerGridModel(input_data={"node": node_input}) + + +def test_handle_invalid_measured_object_error(): + node_input = initialize_array("input", "node", 2) + node_input["id"] = [0, 1] + node_input["u_rated"] = [0.0, 0.0] + + link_input = initialize_array("input", "link", 1) + link_input["id"] = [2] + link_input["from_node"] = [0] + link_input["to_node"] = [1] + + sym_power_sensor_input = initialize_array("input", "sym_power_sensor", 1) + sym_power_sensor_input["id"] = [3] + sym_power_sensor_input["measured_object"] = [2] + sym_power_sensor_input["measured_terminal_type"] = [MeasuredTerminalType.branch_from] + + with pytest.raises(InvalidMeasuredObject): + PowerGridModel(input_data={"node": node_input, "link": link_input, "sym_power_sensor": sym_power_sensor_input}) + + +def test_handle_id_wrong_type_error(): + node_input = initialize_array("input", "node", 1) + node_input["id"] = [0] + node_input["u_rated"] = [0.0] + + sym_power_sensor_input = initialize_array("input", "sym_power_sensor", 1) + sym_power_sensor_input["id"] = [1] + sym_power_sensor_input["measured_object"] = [0] + sym_power_sensor_input["measured_terminal_type"] = [MeasuredTerminalType.branch_from] + + with pytest.raises(IDWrongType): + PowerGridModel(input_data={"node": node_input, "sym_power_sensor": sym_power_sensor_input}) + + +def test_handle_invalid_calculation_method_error(): + node_input = initialize_array("input", "node", 1) + node_input["id"] = [0] + node_input["u_rated"] = [10.0e3] + + source_input = initialize_array("input", "source", 1) + source_input["id"] = [1] + source_input["node"] = [0] + source_input["status"] = [1] + source_input["u_ref"] = [10.0e3] + + sym_load_input = initialize_array("input", "sym_load", 1) + sym_load_input["id"] = [2] + sym_load_input["node"] = [0] + sym_load_input["status"] = [1] + sym_load_input["type"] = [LoadGenType.const_power] + sym_load_input["p_specified"] = [10.0e3] + sym_load_input["q_specified"] = [1.0e3] + + model = PowerGridModel(input_data={"node": node_input, "source": source_input, "sym_load": sym_load_input}) + with pytest.raises(InvalidCalculationMethod): + model.calculate_power_flow(calculation_method=CalculationMethod.iec60909) From 22a9ea59d9983dceb99e69b3de238b90d6d1e126 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 26 Mar 2024 08:39:25 +0100 Subject: [PATCH 03/14] add error decoding to PGM interface Signed-off-by: Martijn Govers --- src/power_grid_model/core/power_grid_model.py | 36 +++++++++++++++---- tests/unit/utils.py | 32 +++++++++++++++-- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/power_grid_model/core/power_grid_model.py b/src/power_grid_model/core/power_grid_model.py index 56410aed5d..1f8cb6443a 100644 --- a/src/power_grid_model/core/power_grid_model.py +++ b/src/power_grid_model/core/power_grid_model.py @@ -200,8 +200,10 @@ def as_enum_value(key_enum: str, type_: Type[IntEnum]): setattr(opt, key, value.value if isinstance(value, IntEnum) else value) return opt - def _handle_errors(self, continue_on_batch_error: bool, batch_size: int): - self._batch_error = handle_errors(continue_on_batch_error=continue_on_batch_error, batch_size=batch_size) + def _handle_errors(self, continue_on_batch_error: bool, batch_size: int, decode_error: bool): + self._batch_error = handle_errors( + continue_on_batch_error=continue_on_batch_error, batch_size=batch_size, decode_error=decode_error + ) # pylint: disable=too-many-arguments def _calculate_impl( @@ -212,6 +214,7 @@ def _calculate_impl( output_component_types: Optional[Union[Set[str], List[str]]], options: Options, continue_on_batch_error: bool, + decode_error: bool, ): """ Core calculation routine @@ -223,6 +226,7 @@ def _calculate_impl( output_component_types: options: continue_on_batch_error: + decode_error: Returns: """ @@ -258,7 +262,9 @@ def _calculate_impl( update_data=update_ptr, ) - self._handle_errors(continue_on_batch_error=continue_on_batch_error, batch_size=batch_size) + self._handle_errors( + continue_on_batch_error=continue_on_batch_error, batch_size=batch_size, decode_error=decode_error + ) return output_data @@ -273,6 +279,7 @@ def _calculate_power_flow( threading: int = -1, output_component_types: Optional[Union[Set[str], List[str]]] = None, continue_on_batch_error: bool = False, + decode_error: bool = True, experimental_features: Union[_ExperimentalFeatures, str] = _ExperimentalFeatures.disabled, ): calculation_type = CalculationType.power_flow @@ -292,6 +299,7 @@ def _calculate_power_flow( output_component_types=output_component_types, options=options, continue_on_batch_error=continue_on_batch_error, + decode_error=decode_error, ) def _calculate_state_estimation( @@ -305,6 +313,7 @@ def _calculate_state_estimation( threading: int = -1, output_component_types: Optional[Union[Set[str], List[str]]] = None, continue_on_batch_error: bool = False, + decode_error: bool = True, experimental_features: Union[_ExperimentalFeatures, str] = _ExperimentalFeatures.disabled, ) -> Dict[str, np.ndarray]: calculation_type = CalculationType.state_estimation @@ -324,6 +333,7 @@ def _calculate_state_estimation( output_component_types=output_component_types, options=options, continue_on_batch_error=continue_on_batch_error, + decode_error=decode_error, ) def _calculate_short_circuit( @@ -334,6 +344,7 @@ def _calculate_short_circuit( threading: int = -1, output_component_types: Optional[Union[Set[str], List[str]]] = None, continue_on_batch_error: bool = False, + decode_error: bool = True, short_circuit_voltage_scaling: Union[ShortCircuitVoltageScaling, str] = ShortCircuitVoltageScaling.maximum, experimental_features: Union[_ExperimentalFeatures, str] = _ExperimentalFeatures.disabled, ) -> Dict[str, np.ndarray]: @@ -355,6 +366,7 @@ def _calculate_short_circuit( output_component_types=output_component_types, options=options, continue_on_batch_error=continue_on_batch_error, + decode_error=decode_error, ) def calculate_power_flow( @@ -368,6 +380,7 @@ def calculate_power_flow( threading: int = -1, output_component_types: Optional[Union[Set[str], List[str]]] = None, continue_on_batch_error: bool = False, + decode_error: bool = True, ) -> Dict[str, np.ndarray]: """ Calculate power flow once with the current model attributes. @@ -411,8 +424,10 @@ def calculate_power_flow( - > 0: Specify number of parallel threads output_component_types ({set, list}, optional): List or set of component types you want to be present in the output dict. By default, all component types will be in the output. - continue_on_batch_error (bool, optional): If the program continues (instead of throwing error) if some + continue_on_batch_error (bool, optional): Continue the program (instead of throwing error) if some scenarios fail. + decode_error (bool, optional): + Decode error messages to their derived types if possible. Returns: Dictionary of results of all components. @@ -438,6 +453,7 @@ def calculate_power_flow( threading=threading, output_component_types=output_component_types, continue_on_batch_error=continue_on_batch_error, + decode_error=decode_error, ) def calculate_state_estimation( @@ -451,6 +467,7 @@ def calculate_state_estimation( threading: int = -1, output_component_types: Optional[Union[Set[str], List[str]]] = None, continue_on_batch_error: bool = False, + decode_error: bool = True, ) -> Dict[str, np.ndarray]: """ Calculate state estimation once with the current model attributes. @@ -491,8 +508,10 @@ def calculate_state_estimation( - > 0: Specify number of parallel threads output_component_types ({set, list}, optional): List or set of component types you want to be present in the output dict. By default, all component types will be in the output. - continue_on_batch_error (bool, optional): If the program continues (instead of throwing error) if some + continue_on_batch_error (bool, optional): Continue the program (instead of throwing error) if some scenarios fail. + decode_error (bool, optional): + Decode error messages to their derived types if possible. Returns: Dictionary of results of all components. @@ -518,6 +537,7 @@ def calculate_state_estimation( threading=threading, output_component_types=output_component_types, continue_on_batch_error=continue_on_batch_error, + decode_error=decode_error, ) def calculate_short_circuit( @@ -528,6 +548,7 @@ def calculate_short_circuit( threading: int = -1, output_component_types: Optional[Union[Set[str], List[str]]] = None, continue_on_batch_error: bool = False, + decode_error: bool = True, short_circuit_voltage_scaling: Union[ShortCircuitVoltageScaling, str] = ShortCircuitVoltageScaling.maximum, ) -> Dict[str, np.ndarray]: """ @@ -563,7 +584,9 @@ def calculate_short_circuit( List or set of component types you want to be present in the output dict. By default, all component types will be in the output. continue_on_batch_error (bool, optional): - If the program continues (instead of throwing error) if some scenarios fail. + Continue the program (instead of throwing error) if some scenarios fail. + decode_error (bool, optional): + Decode error messages to their derived types if possible. short_circuit_voltage_scaling ({ShortCircuitVoltageSaling, str}, optional): Whether to use the maximum or minimum voltage scaling. By default, the maximum voltage scaling is used to calculate the short circuit. @@ -588,6 +611,7 @@ def calculate_short_circuit( threading=threading, output_component_types=output_component_types, continue_on_batch_error=continue_on_batch_error, + decode_error=decode_error, short_circuit_voltage_scaling=short_circuit_voltage_scaling, ) diff --git a/tests/unit/utils.py b/tests/unit/utils.py index a3523c8d64..32c67eb55b 100644 --- a/tests/unit/utils.py +++ b/tests/unit/utils.py @@ -13,7 +13,20 @@ from power_grid_model.core.power_grid_model import PowerGridModel from power_grid_model.data_types import Dataset, PythonDataset, SingleDataset -from power_grid_model.errors import PowerGridBatchError, PowerGridError, PowerGridSerializationError +from power_grid_model.errors import ( + ConflictID, + ConflictVoltage, + IDWrongType, + InvalidBranch, + InvalidBranch3, + InvalidCalculationMethod, + InvalidMeasuredObject, + InvalidTransformerClock, + NotObservableError, + PowerGridBatchError, + PowerGridError, + PowerGridSerializationError, +) from power_grid_model.utils import json_deserialize, json_deserialize_from_file, json_serialize_to_file BASE_PATH = Path(__file__).parent.parent @@ -25,7 +38,22 @@ KNOWN_EXCEPTIONS = { ex.__name__: ex - for ex in (PowerGridBatchError, PowerGridError, PowerGridSerializationError, AssertionError, OSError) + for ex in ( + PowerGridBatchError, + PowerGridError, + ConflictID, + ConflictVoltage, + IDWrongType, + InvalidBranch, + InvalidBranch3, + InvalidCalculationMethod, + InvalidMeasuredObject, + InvalidTransformerClock, + NotObservableError, + PowerGridSerializationError, + AssertionError, + OSError, + ) } From c449d14887d7ae07b1fa8aaa763b851eee4c2270 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 26 Mar 2024 10:18:35 +0100 Subject: [PATCH 04/14] fix sonar cloud Signed-off-by: Martijn Govers --- src/power_grid_model/core/error_handling.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/power_grid_model/core/error_handling.py b/src/power_grid_model/core/error_handling.py index bba89790bb..d45e0b0b4e 100644 --- a/src/power_grid_model/core/error_handling.py +++ b/src/power_grid_model/core/error_handling.py @@ -53,9 +53,7 @@ _INVALID_TRANSFORMER_CLOCK_RE = re.compile(r"Invalid clock for transformer (-?\d+), clock (-?\d+)\n") _SPARSE_MATRIX_ERROR_RE = re.compile(r"Sparse matrix error") # multiple different flavors _NOT_OBSERVABLE_ERROR_RE = re.compile(r"Not enough measurements available for state estimation.\n") -_ITERATION_DIVERGE_RE = re.compile( - r"Iteration failed to converge after (-?\d+) iterations! Max deviation: (.*), error tolerance: (.*).\n" -) +_ITERATION_DIVERGE_RE = re.compile(r"Iteration failed to converge") # potentially multiple different flavors _CONFLICT_ID_RE = re.compile(r"Conflicting id detected: (-?\d+)\n") _ID_NOT_FOUND_RE = re.compile(r"The id cannot be found: (-?\d+)\n") _INVALID_MEASURED_OBJECT_RE = re.compile(r"(\w+) is not supported for (\w+)") From 36015c7e882e29a2336a0302d46e54f282e4f00a Mon Sep 17 00:00:00 2001 From: Tony Xiang Date: Wed, 21 Feb 2024 22:40:45 +0100 Subject: [PATCH 05/14] reproduce error Signed-off-by: Tony Xiang --- .../newton-raphson-initial-value/input.json | 23 +++++++++++++++++++ .../input.json.license | 3 +++ .../newton-raphson-initial-value/params.json | 5 ++++ .../params.json.license | 3 +++ .../sym_output.json | 13 +++++++++++ .../sym_output.json.license | 3 +++ 6 files changed, 50 insertions(+) create mode 100644 tests/data/power_flow/newton-raphson-initial-value/input.json create mode 100644 tests/data/power_flow/newton-raphson-initial-value/input.json.license create mode 100644 tests/data/power_flow/newton-raphson-initial-value/params.json create mode 100644 tests/data/power_flow/newton-raphson-initial-value/params.json.license create mode 100644 tests/data/power_flow/newton-raphson-initial-value/sym_output.json create mode 100644 tests/data/power_flow/newton-raphson-initial-value/sym_output.json.license diff --git a/tests/data/power_flow/newton-raphson-initial-value/input.json b/tests/data/power_flow/newton-raphson-initial-value/input.json new file mode 100644 index 0000000000..bb7a4b703a --- /dev/null +++ b/tests/data/power_flow/newton-raphson-initial-value/input.json @@ -0,0 +1,23 @@ +{ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 0, "u_rated": 150000}, + {"id": 2, "u_rated": 10500}, + {"id": 4, "u_rated": 10500} + ], + "transformer": [ + {"id": 10, "from_node": 0, "to_node": 2, "from_status": 1, "to_status": 1, "u1": 150000, "u2": 11000, "sn": 12000000, "uk": 0.204, "pk": 60000, "i0": 0, "p0": 0, "winding_from": 1, "winding_to": 2, "clock": 5, "tap_side": 0, "tap_pos": -11, "tap_min": -11, "tap_max": 9, "tap_nom": 0, "tap_size": 2500, "uk_min": 0.204, "uk_max": 0.204, "pk_min": 60000, "pk_max": 60000, "r_grounding_from": 0, "x_grounding_from": 0}, + {"id": 13, "from_node": 2, "to_node": 4, "from_status": 1, "to_status": 1, "u1": 10500, "u2": 10500, "sn": 12000000, "uk": 0.005, "pk": 0, "i0": 0, "p0": 0, "winding_from": 0, "winding_to": 0, "clock": 0, "tap_side": 0, "tap_pos": 1, "tap_min": 1, "tap_max": 13, "tap_nom": 7, "tap_size": 150, "uk_min": 0.004500000000000001, "uk_max": 0.006, "pk_min": 0, "pk_max": 0, "r_grounding_from": 0, "x_grounding_from": 0, "r_grounding_to": 0, "x_grounding_to": 0} + ], + "source": [ + {"id": 14, "node": 0, "status": 1, "u_ref": 1, "sk": 1e+12, "rx_ratio": 0.1, "z01_ratio": 3} + ], + "sym_load": [ + {"id": 17, "node": 4, "status": 1, "type": 0, "p_specified": 0, "q_specified": 0} + ] + } +} \ No newline at end of file diff --git a/tests/data/power_flow/newton-raphson-initial-value/input.json.license b/tests/data/power_flow/newton-raphson-initial-value/input.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/power_flow/newton-raphson-initial-value/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/power_flow/newton-raphson-initial-value/params.json b/tests/data/power_flow/newton-raphson-initial-value/params.json new file mode 100644 index 0000000000..1758791a12 --- /dev/null +++ b/tests/data/power_flow/newton-raphson-initial-value/params.json @@ -0,0 +1,5 @@ +{ + "calculation_method": ["newton_raphson", "iterative_current", "linear", "linear_current"], + "rtol": 1e-8, + "atol": 1e-6 +} \ No newline at end of file diff --git a/tests/data/power_flow/newton-raphson-initial-value/params.json.license b/tests/data/power_flow/newton-raphson-initial-value/params.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/power_flow/newton-raphson-initial-value/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/power_flow/newton-raphson-initial-value/sym_output.json b/tests/data/power_flow/newton-raphson-initial-value/sym_output.json new file mode 100644 index 0000000000..28011ed68b --- /dev/null +++ b/tests/data/power_flow/newton-raphson-initial-value/sym_output.json @@ -0,0 +1,13 @@ +{ + "version": "1.0", + "type": "sym_output", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + {"id": 0, "u": 150000}, + {"id": 2, "u": 13469.38775510207}, + {"id": 4, "u": 14732.14285714289} + ] + } +} \ No newline at end of file diff --git a/tests/data/power_flow/newton-raphson-initial-value/sym_output.json.license b/tests/data/power_flow/newton-raphson-initial-value/sym_output.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/power_flow/newton-raphson-initial-value/sym_output.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 From 106939f1879c5494a75478677855f2e0754067bc Mon Sep 17 00:00:00 2001 From: Nitish Bharambe Date: Mon, 18 Mar 2024 15:26:33 +0100 Subject: [PATCH 06/14] wip refactor linear solver Signed-off-by: Nitish Bharambe --- .../math_solver/common_solver_functions.hpp | 24 +++++++++++++++++++ .../iterative_current_pf_solver.hpp | 24 ++++++++++++++++++- .../math_solver/iterative_pf_solver.hpp | 20 +--------------- .../math_solver/linear_pf_solver.hpp | 20 +--------------- .../math_solver/newton_raphson_pf_solver.hpp | 13 +++++++++- 5 files changed, 61 insertions(+), 40 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/common_solver_functions.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/common_solver_functions.hpp index 7c9116f8fe..946bba6f83 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/common_solver_functions.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/common_solver_functions.hpp @@ -23,6 +23,30 @@ inline void add_sources(IdxRange const& sources, Idx /* bus_number */, YBus } } +template +inline void add_linear_loads(boost::iterator_range const& load_gens_per_bus, Idx /* bus_number */, + PowerFlowInput const& input, ComplexTensor& diagonal_element) { + for (auto load_number : load_gens_per_bus) { + // YBus_diag += -conj(S_base) + add_diag(diagonal_element, -conj(input.s_injection[load_number])); + } +} + +template +inline void prepare_linear_matrix_and_rhs(YBus const& y_bus, PowerFlowInput const& input, + grouped_idx_vector_type auto const& load_gens_per_bus, + grouped_idx_vector_type auto const& sources_per_bus, MathOutput& output, + ComplexTensorVector& mat_data) { + IdxVector const& bus_entry = y_bus.lu_diag(); + for (auto const& [bus_number, load_gens, sources] : enumerated_zip_sequence(load_gens_per_bus, sources_per_bus)) { + Idx const diagonal_position = bus_entry[bus_number]; + auto& diagonal_element = mat_data[diagonal_position]; + auto& u_bus = output.u[bus_number]; + add_linear_loads(load_gens, bus_number, input, diagonal_element); + add_sources(sources, bus_number, y_bus, input.source, diagonal_element, u_bus); + } +} + template inline void copy_y_bus(YBus const& y_bus, ComplexTensorVector& mat_data) { ComplexTensorVector const& ydata = y_bus.admittance(); std::transform(y_bus.map_lu_y_bus().cbegin(), y_bus.map_lu_y_bus().cend(), mat_data.begin(), [&](Idx k) { diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp index befaec2270..c9598b2001 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp @@ -82,7 +82,9 @@ class IterativeCurrentPFSolver : public IterativePFSolver const& y_bus, MathOutput const& /* output */) { + void initialize_derived_solver(YBus const& y_bus, PowerFlowInput const& input, MathOutput& output) { + make_flat_start(input, output.u); + auto const& sources_per_bus = *this->sources_per_bus_; IdxVector const& bus_entry = y_bus.lu_diag(); // if Y bus is not up to date @@ -187,6 +189,26 @@ class IterativeCurrentPFSolver : public IterativePFSolver{input.source[source_number]}); } } + + void make_flat_start(PowerFlowInput const& input, ComplexValueVector& output_u) { + std::vector const& phase_shift = *this->phase_shift_; + // average u_ref of all sources + DoubleComplex const u_ref = [&]() { + DoubleComplex sum_u_ref = 0.0; + for (auto const& [bus, sources] : enumerated_zip_sequence(*this->sources_per_bus_)) { + for (Idx const source : sources) { + sum_u_ref += input.source[source] * std::exp(1.0i * -phase_shift[bus]); // offset phase shift + } + } + return sum_u_ref / (double)input.source.size(); + }(); + + // assign u_ref as flat start + for (Idx i = 0; i != this->n_bus_; ++i) { + // consider phase shift + output_u[i] = ComplexValue{u_ref * std::exp(1.0i * phase_shift[i])}; + } + } }; template class IterativeCurrentPFSolver; diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_pf_solver.hpp index c09c96dab4..1a6b6ea681 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_pf_solver.hpp @@ -28,7 +28,6 @@ template class IterativePFSolver { Idx max_iter, CalculationInfo& calculation_info) { // get derived reference for derived solver class auto derived_solver = static_cast(*this); - std::vector const& phase_shift = *phase_shift_; // prepare MathOutput output; @@ -40,25 +39,8 @@ template class IterativePFSolver { // initialize { Timer const sub_timer{calculation_info, 2221, "Initialize calculation"}; - // average u_ref of all sources - DoubleComplex const u_ref = [&]() { - DoubleComplex sum_u_ref = 0.0; - for (auto const& [bus, sources] : enumerated_zip_sequence(*sources_per_bus_)) { - for (Idx const source : sources) { - sum_u_ref += input.source[source] * std::exp(1.0i * -phase_shift[bus]); // offset phase shift - } - } - return sum_u_ref / (double)input.source.size(); - }(); - - // assign u_ref as flat start - for (Idx i = 0; i != n_bus_; ++i) { - // consider phase shift - output.u[i] = ComplexValue{u_ref * std::exp(1.0i * phase_shift[i])}; - } - // Further initialization specific to the derived solver - derived_solver.initialize_derived_solver(y_bus, output); + derived_solver.initialize_derived_solver(y_bus, input, output); } // start calculation diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp index 6b10cda197..513c6b8be9 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp @@ -91,25 +91,7 @@ template class LinearPFSolver { typename SparseLUSolver, ComplexValue, ComplexValue>::BlockPermArray perm_; void prepare_matrix_and_rhs(YBus const& y_bus, PowerFlowInput const& input, MathOutput& output) { - using detail::add_sources; - - IdxVector const& bus_entry = y_bus.lu_diag(); - for (auto const& [bus_number, load_gens, sources] : - enumerated_zip_sequence(*load_gens_per_bus_, *sources_per_bus_)) { - Idx const diagonal_position = bus_entry[bus_number]; - auto& diagonal_element = mat_data_[diagonal_position]; - auto& u_bus = output.u[bus_number]; - add_loads(load_gens, bus_number, input, diagonal_element); - add_sources(sources, bus_number, y_bus, input.source, diagonal_element, u_bus); - } - } - - static void add_loads(boost::iterator_range const& load_gens_per_bus, Idx /* bus_number */, - PowerFlowInput const& input, ComplexTensor& diagonal_element) { - for (auto load_number : load_gens_per_bus) { - // YBus_diag += -conj(S_base) - add_diag(diagonal_element, -conj(input.s_injection[load_number])); - } + detail::prepare_linear_matrix_and_rhs(y_bus, input, *load_gens_per_bus_, *sources_per_bus_, output, mat_data_); } void calculate_result(YBus const& y_bus, PowerFlowInput const& input, MathOutput& output) { diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp index 2814203d53..759ae95701 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp @@ -211,7 +211,18 @@ template class NewtonRaphsonPFSolver : public IterativePFSolv perm_(y_bus.size()) {} // Initilize the unknown variable in polar form - void initialize_derived_solver(YBus const& /* y_bus */, MathOutput const& output) { + void initialize_derived_solver(YBus const& y_bus, PowerFlowInput const& input, MathOutput& output) { + ComplexTensorVector linear_mat_data(y_bus.nnz_lu()); + SparseLUSolver, ComplexValue, ComplexValue> linear_sparse_solver{ + y_bus.shared_indptr_lu(), y_bus.shared_indices_lu(), y_bus.shared_diag_lu()}; + typename SparseLUSolver, ComplexValue, ComplexValue>::BlockPermArray linear_perm( + y_bus.size()); + + detail::prepare_linear_matrix_and_rhs(y_bus, input, *this->load_gens_per_bus_, *this->sources_per_bus_, output, + linear_mat_data); + detail::copy_y_bus(y_bus, linear_mat_data); + linear_sparse_solver.prefactorize_and_solve(linear_mat_data, linear_perm, output.u, output.u); + // get magnitude and angle of start voltage for (Idx i = 0; i != this->n_bus_; ++i) { x_[i].v() = cabs(output.u[i]); From 009c1b1242268a4378d1a6f8f175bde246a00dd0 Mon Sep 17 00:00:00 2001 From: Nitish Bharambe Date: Mon, 18 Mar 2024 22:18:44 +0100 Subject: [PATCH 07/14] move copy ybus pos Signed-off-by: Nitish Bharambe --- .../power_grid_model/math_solver/newton_raphson_pf_solver.hpp | 2 +- tests/cpp_validation_tests/test_validation.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp index 759ae95701..f64f299d99 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp @@ -218,9 +218,9 @@ template class NewtonRaphsonPFSolver : public IterativePFSolv typename SparseLUSolver, ComplexValue, ComplexValue>::BlockPermArray linear_perm( y_bus.size()); + detail::copy_y_bus(y_bus, linear_mat_data); detail::prepare_linear_matrix_and_rhs(y_bus, input, *this->load_gens_per_bus_, *this->sources_per_bus_, output, linear_mat_data); - detail::copy_y_bus(y_bus, linear_mat_data); linear_sparse_solver.prefactorize_and_solve(linear_mat_data, linear_perm, output.u, output.u); // get magnitude and angle of start voltage diff --git a/tests/cpp_validation_tests/test_validation.cpp b/tests/cpp_validation_tests/test_validation.cpp index fcde9f6e6c..0b35471486 100644 --- a/tests/cpp_validation_tests/test_validation.cpp +++ b/tests/cpp_validation_tests/test_validation.cpp @@ -292,6 +292,7 @@ std::map const calculation_method_mapping = { {"linear", CalculationMethod::linear}, {"iterative_current", CalculationMethod::iterative_current}, {"iterative_linear", CalculationMethod::iterative_linear}, + {"linear_current", CalculationMethod::linear_current}, {"iec60909", CalculationMethod::iec60909}, }; std::map const sc_voltage_scaling_mapping = { From 1bd1cb6d13e2dfbaf6146acde5736a34a3bb4426 Mon Sep 17 00:00:00 2001 From: Nitish Bharambe Date: Tue, 19 Mar 2024 17:13:12 +0100 Subject: [PATCH 08/14] use using Signed-off-by: Nitish Bharambe --- .../math_solver/iterative_current_pf_solver.hpp | 7 ++++--- .../math_solver/linear_pf_solver.hpp | 5 +++-- .../math_solver/newton_raphson_pf_solver.hpp | 15 +++++++++------ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp index c9598b2001..4daed5e8cd 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp @@ -73,8 +73,8 @@ namespace iterative_current_pf { template class IterativeCurrentPFSolver : public IterativePFSolver> { public: - using BlockPermArray = - typename SparseLUSolver, ComplexValue, ComplexValue>::BlockPermArray; + using SparseSolverType = SparseLUSolver, ComplexValue, ComplexValue>; + using BlockPermArray = typename SparseSolverType::BlockPermArray; IterativeCurrentPFSolver(YBus const& y_bus, std::shared_ptr const& topo_ptr) : IterativePFSolver{y_bus, topo_ptr}, @@ -151,7 +151,8 @@ class IterativeCurrentPFSolver : public IterativePFSolver rhs_u_; std::shared_ptr const> mat_data_; // sparse solver - SparseLUSolver, ComplexValue, ComplexValue> sparse_solver_; + using SparseSolverType = SparseLUSolver, ComplexValue, ComplexValue>; + SparseSolverType sparse_solver_; std::shared_ptr perm_; bool parameters_changed_ = true; diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp index 513c6b8be9..6e5647f615 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp @@ -87,8 +87,9 @@ template class LinearPFSolver { // sparse linear equation ComplexTensorVector mat_data_; // sparse solver - SparseLUSolver, ComplexValue, ComplexValue> sparse_solver_; - typename SparseLUSolver, ComplexValue, ComplexValue>::BlockPermArray perm_; + using SparseSolverType = SparseLUSolver, ComplexValue, ComplexValue>; + SparseSolverType sparse_solver_; + typename SparseSolverType::BlockPermArray perm_; void prepare_matrix_and_rhs(YBus const& y_bus, PowerFlowInput const& input, MathOutput& output) { detail::prepare_linear_matrix_and_rhs(y_bus, input, *load_gens_per_bus_, *sources_per_bus_, output, mat_data_); diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp index f64f299d99..d0914e9860 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp @@ -212,11 +212,12 @@ template class NewtonRaphsonPFSolver : public IterativePFSolv // Initilize the unknown variable in polar form void initialize_derived_solver(YBus const& y_bus, PowerFlowInput const& input, MathOutput& output) { + using LinearSparseSolverType = SparseLUSolver, ComplexValue, ComplexValue>; + ComplexTensorVector linear_mat_data(y_bus.nnz_lu()); - SparseLUSolver, ComplexValue, ComplexValue> linear_sparse_solver{ - y_bus.shared_indptr_lu(), y_bus.shared_indices_lu(), y_bus.shared_diag_lu()}; - typename SparseLUSolver, ComplexValue, ComplexValue>::BlockPermArray linear_perm( - y_bus.size()); + LinearSparseSolverType linear_sparse_solver{y_bus.shared_indptr_lu(), y_bus.shared_indices_lu(), + y_bus.shared_diag_lu()}; + typename LinearSparseSolverType::BlockPermArray linear_perm(y_bus.size()); detail::copy_y_bus(y_bus, linear_mat_data); detail::prepare_linear_matrix_and_rhs(y_bus, input, *this->load_gens_per_bus_, *this->sources_per_bus_, output, @@ -280,9 +281,11 @@ template class NewtonRaphsonPFSolver : public IterativePFSolv // 2. power unbalance: p/q_specified - p/q_calculated // 3. unknown iterative std::vector> del_x_pq_; - SparseLUSolver, ComplexPower, PolarPhasor> sparse_solver_; + + using SparseSolverType = SparseLUSolver, ComplexPower, PolarPhasor>; + SparseSolverType sparse_solver_; // permutation array - typename SparseLUSolver, ComplexPower, PolarPhasor>::BlockPermArray perm_; + typename SparseSolverType::BlockPermArray perm_; /// @brief power_flow_ij = (ui @* conj(uj)) .* conj(yij) /// Hij = diag(Vi) * ( Gij .* sin(theta_ij) - Bij .* cos(theta_ij) ) * diag(Vj) From 563ba21ba7f54088c0699d64dc8319946c435a89 Mon Sep 17 00:00:00 2001 From: Nitish Bharambe Date: Tue, 19 Mar 2024 20:09:39 +0100 Subject: [PATCH 09/14] remove duplicate declaration Signed-off-by: Nitish Bharambe --- .../power_grid_model/math_solver/iterative_current_pf_solver.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp index 4daed5e8cd..1a0064b7b4 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp @@ -151,7 +151,6 @@ class IterativeCurrentPFSolver : public IterativePFSolver rhs_u_; std::shared_ptr const> mat_data_; // sparse solver - using SparseSolverType = SparseLUSolver, ComplexValue, ComplexValue>; SparseSolverType sparse_solver_; std::shared_ptr perm_; bool parameters_changed_ = true; From 1a0a83fb1d01424f80b36a1762eb9792b80bb82b Mon Sep 17 00:00:00 2001 From: Nitish Bharambe Date: Wed, 20 Mar 2024 09:58:40 +0100 Subject: [PATCH 10/14] remove source as sk is too high Signed-off-by: Nitish Bharambe --- tests/data/power_flow/1os2msr/sym_output.json | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/data/power_flow/1os2msr/sym_output.json b/tests/data/power_flow/1os2msr/sym_output.json index 67f74d7604..5f75fd1c2b 100644 --- a/tests/data/power_flow/1os2msr/sym_output.json +++ b/tests/data/power_flow/1os2msr/sym_output.json @@ -55,17 +55,6 @@ "s_to": 1.043456e+06 } ], - "source": [ - { - "id": 6, - "energized": 1, - "p": 2.401665e+06, - "q": -2.946045e+06, - "i": 206.830905, - "s": 3.800944e+06, - "pf": 0.63186 - } - ], "sym_load": [ { "id": 7, From 7763aeeb3222395f3667015118e8d64cf9d49483 Mon Sep 17 00:00:00 2001 From: Nitish Bharambe Date: Tue, 26 Mar 2024 14:48:22 +0100 Subject: [PATCH 11/14] address comments Signed-off-by: Nitish Bharambe --- .../math_solver/iterative_current_pf_solver.hpp | 2 +- .../power_grid_model/math_solver/iterative_pf_solver.hpp | 2 +- .../power_grid_model/math_solver/linear_pf_solver.hpp | 7 +++++-- .../math_solver/newton_raphson_pf_solver.hpp | 7 +++++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp index 1a0064b7b4..9e1489dcfe 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_current_pf_solver.hpp @@ -200,7 +200,7 @@ class IterativeCurrentPFSolver : public IterativePFSolver(input.source.size()); }(); // assign u_ref as flat start diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_pf_solver.hpp index 1a6b6ea681..f95f72969a 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/iterative_pf_solver.hpp @@ -76,7 +76,7 @@ template class IterativePFSolver { main_timer.stop(); auto const key = Timer::make_key(2226, "Max number of iterations"); - calculation_info[key] = std::max(calculation_info[key], (double)num_iter); + calculation_info[key] = std::max(calculation_info[key], static_cast(num_iter)); return output; } diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp index 6e5647f615..4fcc381349 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/linear_pf_solver.hpp @@ -45,6 +45,10 @@ namespace linear_pf { template class LinearPFSolver { public: + using SparseSolverType = SparseLUSolver, ComplexValue, ComplexValue>; + using BlockPermArray = + typename SparseLUSolver, ComplexValue, ComplexValue>::BlockPermArray; + LinearPFSolver(YBus const& y_bus, std::shared_ptr const& topo_ptr) : n_bus_{y_bus.size()}, load_gens_per_bus_{topo_ptr, &topo_ptr->load_gens_per_bus}, @@ -87,9 +91,8 @@ template class LinearPFSolver { // sparse linear equation ComplexTensorVector mat_data_; // sparse solver - using SparseSolverType = SparseLUSolver, ComplexValue, ComplexValue>; SparseSolverType sparse_solver_; - typename SparseSolverType::BlockPermArray perm_; + BlockPermArray perm_; void prepare_matrix_and_rhs(YBus const& y_bus, PowerFlowInput const& input, MathOutput& output) { detail::prepare_linear_matrix_and_rhs(y_bus, input, *load_gens_per_bus_, *sources_per_bus_, output, mat_data_); diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp index d0914e9860..c6d5f5a229 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/newton_raphson_pf_solver.hpp @@ -202,6 +202,10 @@ template class PFJacBlock : public Block class NewtonRaphsonPFSolver : public IterativePFSolver> { public: + using SparseSolverType = SparseLUSolver, ComplexPower, PolarPhasor>; + using BlockPermArray = + typename SparseLUSolver, ComplexPower, PolarPhasor>::BlockPermArray; + NewtonRaphsonPFSolver(YBus const& y_bus, std::shared_ptr const& topo_ptr) : IterativePFSolver{y_bus, topo_ptr}, data_jac_(y_bus.nnz_lu()), @@ -282,10 +286,9 @@ template class NewtonRaphsonPFSolver : public IterativePFSolv // 3. unknown iterative std::vector> del_x_pq_; - using SparseSolverType = SparseLUSolver, ComplexPower, PolarPhasor>; SparseSolverType sparse_solver_; // permutation array - typename SparseSolverType::BlockPermArray perm_; + BlockPermArray perm_; /// @brief power_flow_ij = (ui @* conj(uj)) .* conj(yij) /// Hij = diag(Vi) * ( Gij .* sin(theta_ij) - Bij .* cos(theta_ij) ) * diag(Vj) From a3f4ca4ca7a90b8f0dcc10906e16d4a01c954fb1 Mon Sep 17 00:00:00 2001 From: Jerry Guo Date: Tue, 26 Mar 2024 16:01:44 +0100 Subject: [PATCH 12/14] unused param var Signed-off-by: Jerry Guo --- .../power_grid_model/optimizer/tap_position_optimizer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp index e63d465999..4ac42c6009 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp @@ -101,7 +101,7 @@ inline auto get_edge_weights(TransformerGraph const& graph) -> WeightedTrafoList // i. Infinity(INT_MAX), if tap side of the transformer is disconnected. // The transformer regulation should be ignored // ii.Rank of the vertex at the tap side of the transformer, if tap side of the transformer is connected -inline auto rank_transformers(WeightedTrafoList const& w_trafo_list) -> std::vector { return {}; } +inline auto rank_transformers(WeightedTrafoList const& /*w_trafo_list*/) -> std::vector { return {}; } template inline auto rank_transformers(State const& state) -> std::vector { return rank_transformers(get_edge_weights(build_transformer_graph(state))); From e6a3303cec6b1d00098fe0b3c641b980ebf801fd Mon Sep 17 00:00:00 2001 From: Nitish Bharambe Date: Tue, 26 Mar 2024 16:18:22 +0100 Subject: [PATCH 13/14] catch base exception for observability Signed-off-by: Nitish Bharambe --- .../power_grid_model/include/power_grid_model/main_model.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp index 5032d4b0a2..4abb0cecdb 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp @@ -448,6 +448,8 @@ class MainModelImpl, ComponentLis calculation_fn(*this, {}, ignore_output); } catch (const SparseMatrixError&) { // missing entries are provided in the update data + } catch (const NotObservableError&) { + // missing entries are provided in the update data } // error messages From f9a813558df50dd6890ea3b857f7d9539c2243ee Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Wed, 27 Mar 2024 09:52:06 +0100 Subject: [PATCH 14/14] add test test case for state estimation bug #554 Signed-off-by: Martijn Govers --- .../sensor-update-initially-empty/input.json | 31 ++++++++++++ .../input.json.license | 3 ++ .../sensor-update-initially-empty/params.json | 8 +++ .../params.json.license | 3 ++ .../sym_output_batch.json | 50 +++++++++++++++++++ .../sym_output_batch.json.license | 3 ++ .../update_batch.json | 20 ++++++++ .../update_batch.json.license | 3 ++ 8 files changed, 121 insertions(+) create mode 100644 tests/data/state_estimation/sensor-update-initially-empty/input.json create mode 100644 tests/data/state_estimation/sensor-update-initially-empty/input.json.license create mode 100644 tests/data/state_estimation/sensor-update-initially-empty/params.json create mode 100644 tests/data/state_estimation/sensor-update-initially-empty/params.json.license create mode 100644 tests/data/state_estimation/sensor-update-initially-empty/sym_output_batch.json create mode 100644 tests/data/state_estimation/sensor-update-initially-empty/sym_output_batch.json.license create mode 100644 tests/data/state_estimation/sensor-update-initially-empty/update_batch.json create mode 100644 tests/data/state_estimation/sensor-update-initially-empty/update_batch.json.license diff --git a/tests/data/state_estimation/sensor-update-initially-empty/input.json b/tests/data/state_estimation/sensor-update-initially-empty/input.json new file mode 100644 index 0000000000..66d6a85da3 --- /dev/null +++ b/tests/data/state_estimation/sensor-update-initially-empty/input.json @@ -0,0 +1,31 @@ +{ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + { + "id": 1, + "u_rated": 10.0 + } + ], + "source": [ + { + "id": 2, + "node": 1, + "status": 1 + } + ], + "sym_voltage_sensor": [ + { + "id": 3, + "measured_object": 1 + }, + { + "id": 4, + "measured_object": 1 + } + ] + } +} \ No newline at end of file diff --git a/tests/data/state_estimation/sensor-update-initially-empty/input.json.license b/tests/data/state_estimation/sensor-update-initially-empty/input.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/state_estimation/sensor-update-initially-empty/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/sensor-update-initially-empty/params.json b/tests/data/state_estimation/sensor-update-initially-empty/params.json new file mode 100644 index 0000000000..22ff24a540 --- /dev/null +++ b/tests/data/state_estimation/sensor-update-initially-empty/params.json @@ -0,0 +1,8 @@ +{ + "calculation_method": ["iterative_linear", "newton_raphson"], + "rtol": 1e-8, + "atol": { + "default": 1e-8, + ".+_residual": 1e-4 + } +} \ No newline at end of file diff --git a/tests/data/state_estimation/sensor-update-initially-empty/params.json.license b/tests/data/state_estimation/sensor-update-initially-empty/params.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/state_estimation/sensor-update-initially-empty/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/sensor-update-initially-empty/sym_output_batch.json b/tests/data/state_estimation/sensor-update-initially-empty/sym_output_batch.json new file mode 100644 index 0000000000..e39afe378a --- /dev/null +++ b/tests/data/state_estimation/sensor-update-initially-empty/sym_output_batch.json @@ -0,0 +1,50 @@ +{ + "version": "1.0", + "type": "sym_output", + "is_batch": true, + "attributes": {}, + "data": [ + { + "node": [ + { + "id": 1, + "u": 10.570170726436285, + "u_angle": -0.0661620368126038 + } + ], + "sym_voltage_sensor": [ + { + "id": 3, + "u_residual": 0.4298292735637155, + "u_angle_residual": -0.03383796318739621 + }, + { + "id": 4, + "u_residual": -1.5701707264362845, + "u_angle_residual": 0.1661620368126038 + } + ] + }, + { + "node": [ + { + "id": 1, + "u": 10.570170726436285, + "u_angle": -0.0661620368126038 + } + ], + "sym_voltage_sensor": [ + { + "id": 3, + "u_residual": 0.4298292735637155, + "u_angle_residual": -0.03383796318739621 + }, + { + "id": 4, + "u_residual": -1.5701707264362845, + "u_angle_residual": 0.1661620368126038 + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/data/state_estimation/sensor-update-initially-empty/sym_output_batch.json.license b/tests/data/state_estimation/sensor-update-initially-empty/sym_output_batch.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/state_estimation/sensor-update-initially-empty/sym_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/state_estimation/sensor-update-initially-empty/update_batch.json b/tests/data/state_estimation/sensor-update-initially-empty/update_batch.json new file mode 100644 index 0000000000..41dbcfa77c --- /dev/null +++ b/tests/data/state_estimation/sensor-update-initially-empty/update_batch.json @@ -0,0 +1,20 @@ +{ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "sym_voltage_sensor": [ + {"id": 3, "u_measured": 11.0, "u_angle_measured": -0.1, "u_sigma": 1.0}, + {"id": 4, "measured_object": 1, "u_measured": 9.0, "u_angle_measured": 0.1, "u_sigma": 2.0} + ] + }, + { + "sym_voltage_sensor": [ + {"id": 3, "u_measured": 11.0, "u_angle_measured": -0.1, "u_sigma": 1.0}, + {"id": 4, "measured_object": 1, "u_measured": 9.0, "u_angle_measured": 0.1, "u_sigma": 2.0} + ] + } + ] +} \ No newline at end of file diff --git a/tests/data/state_estimation/sensor-update-initially-empty/update_batch.json.license b/tests/data/state_estimation/sensor-update-initially-empty/update_batch.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/state_estimation/sensor-update-initially-empty/update_batch.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0