From 5f7a7cf3376183483a30f1ec059aa67e70f286e3 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 4 Jun 2024 11:20:09 +0200 Subject: [PATCH] skeleton for validation checks for auto tap Signed-off-by: Martijn Govers --- src/power_grid_model/validation/errors.py | 9 ++++++--- src/power_grid_model/validation/rules.py | 13 ++++++++---- src/power_grid_model/validation/validation.py | 20 +++++++++++++++++++ tests/unit/validation/test_rules.py | 11 +++++++++- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/power_grid_model/validation/errors.py b/src/power_grid_model/validation/errors.py index 3f35e0c647..4f2cb5a79f 100644 --- a/src/power_grid_model/validation/errors.py +++ b/src/power_grid_model/validation/errors.py @@ -160,7 +160,7 @@ def field_str(self) -> str: class MultiComponentValidationError(ValidationError): """ - Base class for an error that applies to multiple component, and as a consequence also to multiple fields. + Base class for an error that applies to multiple components, and as a consequence also to multiple fields. Even if both fields have the same name, they are considered to be different fields and notated as such. E.g. the two fields `id` fields of the `node` and `line` component: [('node', 'id'), ('line', 'id')]. """ @@ -237,9 +237,9 @@ class InvalidEnumValueError(SingleFieldValidationError): """ _message = "Field {field} contains invalid {enum} values for {n} {objects}." - enum: Type[Enum] + enum: Union[Type[Enum], List[Type[Enum]]] - def __init__(self, component: str, field: str, ids: List[int], enum: Type[Enum]): + def __init__(self, component: str, field: str, ids: List[int], enum: Union[Type[Enum], List[Type[Enum]]]): super().__init__(component, field, ids) self.enum = enum @@ -248,6 +248,9 @@ def enum_str(self) -> str: """ A string representation of the field to which this error applies. """ + if isinstance(self.enum, list): + return ",".join(e.__name__ for e in self.enum) + return self.enum.__name__ def __eq__(self, other): diff --git a/src/power_grid_model/validation/rules.py b/src/power_grid_model/validation/rules.py index bc21878d33..f61298df75 100644 --- a/src/power_grid_model/validation/rules.py +++ b/src/power_grid_model/validation/rules.py @@ -471,7 +471,7 @@ def all_cross_unique( def all_valid_enum_values( - data: SingleDataset, component: str, field: str, enum: Type[Enum] + data: SingleDataset, component: str, field: str, enum: Union[Type[Enum], List[Type[Enum]]] ) -> List[InvalidEnumValueError]: """ Check that for all records of a particular type of component, the values in the 'field' column are valid values for @@ -481,14 +481,19 @@ def all_valid_enum_values( data (SingleDataset): The input/update data set for all components component (str): The component of interest field (str): The field of interest - enum (Type[Enum]): The enum type to validate against + enum (Type[Enum]): The enum type to validate against, or a list of such enum types Returns: A list containing zero or one InvalidEnumValueError, listing all ids where the value in the field of interest was not a valid value in the supplied enum type. """ - valid = [nan_type(component, field)] + list(enum) - invalid = np.isin(data[component][field], np.array(valid, dtype=np.int8), invert=True) + enums: List[Type[Enum]] = enum if isinstance(enum, list) else [enum] + + valid = {nan_type(component, field)} + for enum_type in enums: + valid.update(list(enum_type)) + + invalid = np.isin(data[component][field], np.array(list(valid), dtype=np.int8), invert=True) if invalid.any(): ids = data[component]["id"][invalid].flatten().tolist() return [InvalidEnumValueError(component, field, ids, enum)] diff --git a/src/power_grid_model/validation/validation.py b/src/power_grid_model/validation/validation.py index 0483fab8ea..9379fdcfff 100644 --- a/src/power_grid_model/validation/validation.py +++ b/src/power_grid_model/validation/validation.py @@ -476,6 +476,9 @@ def validate_values(data: SingleDataset, calculation_type: Optional[CalculationT if calculation_type in (None, CalculationType.short_circuit) and "fault" in data: errors += validate_fault(data) + if calculation_type in (None, CalculationType.power_flow) and "transformer_tap_regulator" in data: + errors += validate_transformer_tap_regulator(data) + return errors @@ -806,3 +809,20 @@ def validate_fault(data: SingleDataset) -> List[ValidationError]: errors += all_enabled_identical(data, "fault", "fault_type", "status") errors += all_enabled_identical(data, "fault", "fault_phase", "status") return errors + + +def validate_transformer_tap_regulator(data: SingleDataset) -> List[ValidationError]: + errors = validate_base(data, "transformer_tap_regulator") + errors += all_valid_ids( + data, + "transformer_tap_regulator", + field="regulated_object", + ref_components=["transformer", "three_winding_transformer"], + ) + errors += all_boolean(data, "transformer_tap_regulator", "status") + errors += all_valid_enum_values(data, "transformer_tap_regulator", "control_side", [BranchSide, Branch3Side]) + errors += all_greater_than_or_equal_to_zero(data, "transformer_tap_regulator", "u_set") + errors += all_greater_than_zero(data, "transformer_tap_regulator", "u_band") + errors += all_greater_than_or_equal_to_zero(data, "transformer_tap_regulator", "line_drop_compensation_r") + errors += all_greater_than_or_equal_to_zero(data, "transformer_tap_regulator", "line_drop_compensation_x") + return errors diff --git a/tests/unit/validation/test_rules.py b/tests/unit/validation/test_rules.py index 6440b7d49d..fad1b93091 100644 --- a/tests/unit/validation/test_rules.py +++ b/tests/unit/validation/test_rules.py @@ -8,7 +8,7 @@ import pytest from power_grid_model import LoadGenType, initialize_array -from power_grid_model.enum import FaultPhase, FaultType +from power_grid_model.enum import Branch3Side, BranchSide, FaultPhase, FaultType from power_grid_model.validation.errors import ( ComparisonError, FaultPhaseError, @@ -329,6 +329,15 @@ def test_all_valid_enum_values(): errors = all_valid_enum_values(valid, "sym_load", "type", LoadGenType) assert not errors + valid = {"transformer_tap_regulator": initialize_array("input", "transformer_tap_regulator", 5)} + valid["transformer_tap_regulator"]["id"] = np.arange(5) + valid["transformer_tap_regulator"]["control_side"] = np.arange(-1, 4) + errors = all_valid_enum_values(valid, "transformer_tap_regulator", "control_side", [BranchSide, Branch3Side]) + assert len(errors) == 1 + assert ( + InvalidEnumValueError("transformer_tap_regulator", "control_side", [0, 4], [BranchSide, Branch3Side]) in errors + ) + def test_all_valid_ids(): # This data is for testing purpuse