From 61a446f7da5f7bf008094adf7fb60a5f7dbc624b Mon Sep 17 00:00:00 2001 From: Ayoub LABIDI Date: Wed, 13 Nov 2024 10:27:14 +0100 Subject: [PATCH] First implementation Signed-off-by: Ayoub LABIDI --- .github/workflows/maven.yml | 43 + .gitignore | 14 + .mvn/lombok-config-copy.marker | 0 lombok.config | 1 + pom.xml | 273 +++ .../modification/IFilterService.java | 19 + .../modification/ModificationType.java | 84 + .../NetworkModificationException.java | 178 ++ .../modification/ReactiveVariationMode.java | 15 + .../modification/TapChangerType.java | 26 + .../gridsuite/modification/VariationMode.java | 18 + .../gridsuite/modification/VariationType.java | 15 + .../dto/AbstractEquipmentDeletionInfos.java | 31 + .../dto/AttributeModification.java | 45 + .../dto/BasicEquipmentModificationInfos.java | 28 + .../dto/BatteryCreationInfos.java | 77 + .../dto/BatteryModificationInfos.java | 75 + .../modification/dto/BranchCreationInfos.java | 75 + .../dto/BranchModificationInfos.java | 74 + .../dto/BusbarConnectionCreationInfos.java | 35 + .../dto/BusbarSectionCreationInfos.java | 39 + .../dto/ByFilterDeletionInfos.java | 62 + .../dto/ByFormulaModificationInfos.java | 56 + .../dto/CompositeModificationInfos.java | 44 + .../dto/ConverterStationCreationInfos.java | 58 + .../ConverterStationModificationInfos.java | 57 + .../modification/dto/CouplingDeviceInfos.java | 31 + .../modification/dto/CurrentLimitsInfos.java | 36 + .../dto/CurrentLimitsModificationInfos.java | 36 + .../CurrentTemporaryLimitCreationInfos.java | 32 + ...urrentTemporaryLimitModificationInfos.java | 34 + .../dto/DeleteAttachingLineInfos.java | 72 + .../dto/DeleteVoltageLevelOnLineInfos.java | 66 + .../EquipmentAttributeModificationInfos.java | 96 + .../dto/EquipmentCreationInfos.java | 28 + .../dto/EquipmentDeletionInfos.java | 55 + .../dto/EquipmentModificationInfos.java | 45 + .../modification/dto/FilterEquipments.java | 39 + .../modification/dto/FilterInfos.java | 33 + .../modification/dto/FreePropertyInfos.java | 40 + .../dto/GenerationDispatchInfos.java | 63 + .../dto/GeneratorCreationInfos.java | 126 ++ .../dto/GeneratorModificationInfos.java | 128 ++ .../dto/GeneratorScalingInfos.java | 45 + .../dto/GeneratorsFilterInfos.java | 34 + .../dto/GeneratorsFrequencyReserveInfos.java | 33 + .../dto/GeneratorsWithoutOutageInfos.java | 34 + .../modification/dto/GroovyScriptInfos.java | 47 + .../dto/HvdcLccDeletionInfos.java | 43 + .../dto/IdentifiableAttributes.java | 33 + .../dto/InjectionCreationInfos.java | 44 + .../dto/InjectionModificationInfos.java | 44 + .../dto/LineAttachToVoltageLevelInfos.java | 83 + .../modification/dto/LineCreationInfos.java | 60 + .../dto/LineModificationInfos.java | 60 + .../dto/LineSplitWithVoltageLevelInfos.java | 78 + .../dto/LinesAttachToSplitLinesInfos.java | 77 + .../modification/dto/LoadCreationInfos.java | 56 + .../dto/LoadModificationInfos.java | 55 + .../modification/dto/LoadScalingInfos.java | 45 + .../dto/ModificationByAssignmentInfos.java | 53 + .../modification/dto/ModificationInfos.java | 141 ++ .../dto/OperatingStatusModificationInfos.java | 87 + .../modification/dto/OperationType.java | 15 + .../dto/PhaseTapChangerCreationInfos.java | 30 + .../dto/PhaseTapChangerModificationInfos.java | 29 + .../dto/RatioTapChangerCreationInfos.java | 34 + .../dto/RatioTapChangerModificationInfos.java | 31 + .../ReactiveCapabilityCurveCreationInfos.java | 36 + ...ctiveCapabilityCurveModificationInfos.java | 42 + .../dto/ReactiveLimitsHolderInfos.java | 24 + .../modification/dto/RegulationSide.java | 15 + .../modification/dto/ScalingInfos.java | 35 + .../dto/ScalingVariationInfos.java | 47 + .../dto/ShuntCompensatorCreationInfos.java | 62 + .../ShuntCompensatorModificationInfos.java | 64 + .../dto/ShuntCompensatorType.java | 15 + .../StaticVarCompensatorCreationInfos.java | 104 ++ .../dto/SubstationCreationInfos.java | 52 + .../dto/SubstationModificationInfos.java | 52 + .../SubstationsGeneratorsOrderingInfos.java | 30 + .../dto/TabularCreationInfos.java | 65 + .../dto/TabularModificationInfos.java | 63 + .../dto/TapChangerCreationInfos.java | 54 + .../dto/TapChangerModificationInfos.java | 63 + .../dto/TapChangerStepCreationInfos.java | 45 + .../dto/TemporaryLimitModificationType.java | 16 + .../TwoWindingsTransformerCreationInfos.java | 70 + ...oWindingsTransformerModificationInfos.java | 67 + .../dto/VoltageInitBusModificationInfos.java | 38 + ...VoltageInitGeneratorModificationInfos.java | 34 + .../dto/VoltageInitModificationInfos.java | 61 + ...InitShuntCompensatorModificationInfos.java | 37 + ...StaticVarCompensatorModificationInfos.java | 34 + ...ltageInitTransformerModificationInfos.java | 38 + ...tVscConverterStationModificationInfos.java | 34 + .../dto/VoltageLevelCreationInfos.java | 80 + .../dto/VoltageLevelModificationInfos.java | 62 + .../dto/VoltageRegulationType.java | 15 + .../modification/dto/VscCreationInfos.java | 85 + .../dto/VscModificationInfos.java | 74 + .../annotation/ModificationErrorTypeName.java | 21 + .../dto/byfilter/AbstractAssignmentInfos.java | 42 + .../modification/dto/byfilter/DataType.java | 19 + .../byfilter/assignment/AssignmentInfos.java | 48 + .../assignment/BooleanAssignmentInfos.java | 26 + .../assignment/DoubleAssignmentInfos.java | 26 + .../assignment/EnumAssignmentInfos.java | 26 + .../assignment/IntegerAssignmentInfos.java | 26 + .../assignment/PropertyAssignmentInfos.java | 40 + .../byfilter/equipmentfield/BatteryField.java | 72 + .../byfilter/equipmentfield/FieldUtils.java | 52 + .../equipmentfield/GeneratorField.java | 116 ++ .../byfilter/equipmentfield/LoadField.java | 44 + .../equipmentfield/ShuntCompensatorField.java | 64 + .../TwoWindingsTransformerField.java | 93 + .../equipmentfield/VoltageLevelField.java | 52 + .../dto/byfilter/formula/FormulaInfos.java | 36 + .../dto/byfilter/formula/Operator.java | 20 + .../formula/ReferenceFieldOrValue.java | 50 + .../AbstractBranchModification.java | 234 +++ .../modifications/AbstractModification.java | 38 + .../modifications/AbstractScaling.java | 127 ++ .../modifications/BatteryCreation.java | 178 ++ .../modifications/BatteryModification.java | 188 ++ .../BusbarSectionFinderTraverser.java | 49 + .../modifications/ByFilterDeletion.java | 128 ++ .../modifications/CompositeModification.java | 25 + .../modifications/DeleteAttachingLine.java | 59 + .../DeleteVoltageLevelOnLine.java | 55 + .../EquipmentAttributeModification.java | 181 ++ .../modifications/EquipmentDeletion.java | 93 + .../modifications/GenerationDispatch.java | 651 +++++++ .../modifications/GeneratorCreation.java | 336 ++++ .../modifications/GeneratorModification.java | 493 ++++++ .../modifications/GeneratorScaling.java | 195 ++ .../modifications/GroovyScript.java | 53 + .../LineAttachToVoltageLevel.java | 92 + .../modifications/LineCreation.java | 93 + .../modifications/LineModification.java | 135 ++ .../LineSplitWithVoltageLevel.java | 67 + .../LinesAttachToSplitLines.java | 69 + .../modifications/LoadCreation.java | 104 ++ .../modifications/LoadModification.java | 80 + .../modifications/LoadScaling.java | 152 ++ .../OperatingStatusModification.java | 192 ++ .../ShuntCompensatorCreation.java | 111 ++ .../ShuntCompensatorModification.java | 234 +++ .../StaticVarCompensatorCreation.java | 259 +++ .../modifications/SubstationCreation.java | 46 + .../modifications/SubstationModification.java | 56 + .../modifications/TabularCreation.java | 88 + .../modifications/TabularModification.java | 135 ++ .../TwoWindingsTransformerCreation.java | 209 +++ .../TwoWindingsTransformerModification.java | 688 ++++++++ .../VoltageInitModification.java | 431 +++++ .../modifications/VoltageLevelCreation.java | 40 + .../VoltageLevelModification.java | 166 ++ .../modifications/VscCreation.java | 284 +++ .../modifications/VscModification.java | 362 ++++ .../AbstractModificationByAssignment.java | 366 ++++ .../byfilter/ByFormulaModification.java | 111 ++ .../byfilter/ModificationByAssignment.java | 107 ++ .../modification/utils/ModificationUtils.java | 1572 +++++++++++++++++ .../modification/utils/PropertiesUtils.java | 71 + .../BatteryModificationTest.java | 392 ++++ .../modification/utils/NetworkCreation.java | 557 ++++++ .../modification/utils/NetworkUtil.java | 388 ++++ 168 files changed, 16919 insertions(+) create mode 100644 .github/workflows/maven.yml create mode 100644 .gitignore create mode 100644 .mvn/lombok-config-copy.marker create mode 100644 lombok.config create mode 100644 pom.xml create mode 100644 src/main/java/org/gridsuite/modification/IFilterService.java create mode 100644 src/main/java/org/gridsuite/modification/ModificationType.java create mode 100644 src/main/java/org/gridsuite/modification/NetworkModificationException.java create mode 100644 src/main/java/org/gridsuite/modification/ReactiveVariationMode.java create mode 100644 src/main/java/org/gridsuite/modification/TapChangerType.java create mode 100644 src/main/java/org/gridsuite/modification/VariationMode.java create mode 100644 src/main/java/org/gridsuite/modification/VariationType.java create mode 100644 src/main/java/org/gridsuite/modification/dto/AbstractEquipmentDeletionInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/AttributeModification.java create mode 100644 src/main/java/org/gridsuite/modification/dto/BasicEquipmentModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/BatteryCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/BatteryModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/BranchCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/BranchModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/BusbarConnectionCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/BusbarSectionCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ByFilterDeletionInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ByFormulaModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/CompositeModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ConverterStationCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ConverterStationModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/CouplingDeviceInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/CurrentLimitsInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/CurrentLimitsModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/CurrentTemporaryLimitCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/CurrentTemporaryLimitModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/DeleteAttachingLineInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/DeleteVoltageLevelOnLineInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/EquipmentAttributeModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/EquipmentCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/EquipmentDeletionInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/EquipmentModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/FilterEquipments.java create mode 100644 src/main/java/org/gridsuite/modification/dto/FilterInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/FreePropertyInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/GenerationDispatchInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/GeneratorCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/GeneratorModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/GeneratorScalingInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/GeneratorsFilterInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/GeneratorsFrequencyReserveInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/GeneratorsWithoutOutageInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/GroovyScriptInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/HvdcLccDeletionInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/IdentifiableAttributes.java create mode 100644 src/main/java/org/gridsuite/modification/dto/InjectionCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/InjectionModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/LineAttachToVoltageLevelInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/LineCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/LineModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/LineSplitWithVoltageLevelInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/LinesAttachToSplitLinesInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/LoadCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/LoadModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/LoadScalingInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ModificationByAssignmentInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/OperatingStatusModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/OperationType.java create mode 100644 src/main/java/org/gridsuite/modification/dto/PhaseTapChangerCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/PhaseTapChangerModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/RatioTapChangerCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/RatioTapChangerModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ReactiveCapabilityCurveCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ReactiveCapabilityCurveModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ReactiveLimitsHolderInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/RegulationSide.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ScalingInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ScalingVariationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ShuntCompensatorCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ShuntCompensatorModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/ShuntCompensatorType.java create mode 100644 src/main/java/org/gridsuite/modification/dto/StaticVarCompensatorCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/SubstationCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/SubstationModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/SubstationsGeneratorsOrderingInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/TabularCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/TabularModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/TapChangerCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/TapChangerModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/TapChangerStepCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/TemporaryLimitModificationType.java create mode 100644 src/main/java/org/gridsuite/modification/dto/TwoWindingsTransformerCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/TwoWindingsTransformerModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VoltageInitBusModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VoltageInitGeneratorModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VoltageInitModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VoltageInitShuntCompensatorModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VoltageInitStaticVarCompensatorModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VoltageInitTransformerModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VoltageInitVscConverterStationModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VoltageLevelCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VoltageLevelModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VoltageRegulationType.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VscCreationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/VscModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/annotation/ModificationErrorTypeName.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/AbstractAssignmentInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/DataType.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/assignment/AssignmentInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/assignment/BooleanAssignmentInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/assignment/DoubleAssignmentInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/assignment/EnumAssignmentInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/assignment/IntegerAssignmentInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/assignment/PropertyAssignmentInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/BatteryField.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/FieldUtils.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/GeneratorField.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/LoadField.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/ShuntCompensatorField.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/TwoWindingsTransformerField.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/VoltageLevelField.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/formula/FormulaInfos.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/formula/Operator.java create mode 100644 src/main/java/org/gridsuite/modification/dto/byfilter/formula/ReferenceFieldOrValue.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/AbstractModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/AbstractScaling.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/BatteryCreation.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/BatteryModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/BusbarSectionFinderTraverser.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/ByFilterDeletion.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/CompositeModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/DeleteAttachingLine.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/DeleteVoltageLevelOnLine.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/EquipmentAttributeModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/EquipmentDeletion.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/GenerationDispatch.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/GeneratorCreation.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/GeneratorScaling.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/GroovyScript.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/LineAttachToVoltageLevel.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/LineCreation.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/LineModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/LineSplitWithVoltageLevel.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/LinesAttachToSplitLines.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/LoadCreation.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/LoadModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/LoadScaling.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/OperatingStatusModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/ShuntCompensatorCreation.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/ShuntCompensatorModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/StaticVarCompensatorCreation.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/SubstationCreation.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/SubstationModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/TabularCreation.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/TabularModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/TwoWindingsTransformerCreation.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/TwoWindingsTransformerModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/VoltageInitModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/VoltageLevelCreation.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/VoltageLevelModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/VscCreation.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/VscModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/byfilter/ByFormulaModification.java create mode 100644 src/main/java/org/gridsuite/modification/modifications/byfilter/ModificationByAssignment.java create mode 100644 src/main/java/org/gridsuite/modification/utils/ModificationUtils.java create mode 100644 src/main/java/org/gridsuite/modification/utils/PropertiesUtils.java create mode 100644 src/test/java/org/gridsuite/modification/modifications/BatteryModificationTest.java create mode 100644 src/test/java/org/gridsuite/modification/utils/NetworkCreation.java create mode 100644 src/test/java/org/gridsuite/modification/utils/NetworkUtil.java diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..aace00b --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,43 @@ +name: CI + +on: + push: + branches: + - 'main' + tags: + - 'v[0-9]*' + pull_request: + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Set up JDK 17 + uses: actions/setup-java@v1 + with: + java-version: 17 + + - name: Checkout sources + uses: actions/checkout@v1 + + - name: Build with Maven + run: mvn --batch-mode -P jacoco verify + + - name: Run SonarCloud analysis + run: > + mvn --batch-mode sonar:sonar + -Dsonar.host.url=https://sonarcloud.io + -Dsonar.organization=gridsuite + -Dsonar.projectKey=org.gridsuite:network-modification + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + - name: Broadcast update event + if: github.ref == 'refs/heads/main' + uses: gridsuite/broadcast-event@main + with: + token: ${{ secrets.REPO_ACCESS_TOKEN }} + event-type: filter_updated diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16a2e2c --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# VSCode +/.vscode +/.settings +.classpath +.factorypath +.project + +target/ + +# IntelliJ +/.idea +*.iml + +derby.log diff --git a/.mvn/lombok-config-copy.marker b/.mvn/lombok-config-copy.marker new file mode 100644 index 0000000..e69de29 diff --git a/lombok.config b/lombok.config new file mode 100644 index 0000000..4fa8250 --- /dev/null +++ b/lombok.config @@ -0,0 +1 @@ +import target/configs/powsybl-build-tools.jar!powsybl-build-tools/lombok.config diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c3f8607 --- /dev/null +++ b/pom.xml @@ -0,0 +1,273 @@ + + + + 4.0.0 + + + com.powsybl + powsybl-parent + 18 + + + + org.gridsuite + gridsuite-network-modification + 1.1.0-SNAPSHOT + + jar + Network modification library + A library to apply modifications on a network + http://www.gridsuite.org/ + + + scm:git:https://github.com/gridsuite/network-modification.git + scm:git:https://github.com/gridsuite/network-modification.git + https://github.com/gridsuite/network-modification + + + + + Ayoub LABIDI + ayoub.labidi_externe@rte-france.com + RTE + http://www.rte-france.com + + + + + 34 + 1.0.5 + 5.0.0-alpha.14 + org.gridsuite.modification.server + + 1.19.0 + **/migration/**/* + + 1.0.14 + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + com.powsybl:powsybl-config-classic + + + + + + + + com.google.cloud.tools + jib-maven-plugin + + + org.springframework.boot + spring-boot-maven-plugin + + + io.github.git-commit-id + git-commit-id-maven-plugin + + + + + + + + + com.squareup.okhttp3 + okhttp-bom + ${mockwebserver3.version} + pom + import + + + com.powsybl + powsybl-network-store-client + + ${powsybl-network-store.version} + + + + com.powsybl + powsybl-network-store-iidm-impl + ${powsybl-network-store.version} + + + + com.powsybl + powsybl-network-store-model + ${powsybl-network-store.version} + + + + + org.gridsuite + gridsuite-dependencies + ${gridsuite-dependencies.version} + pom + import + + + + + com.vladmihalcea + db-util + ${db-util.version} + + + + + + + + org.gridsuite + gridsuite-filter + ${filter.version} + + + com.powsybl + powsybl-iidm-api + + + com.powsybl + powsybl-iidm-modification + + + com.powsybl + powsybl-network-store-client + + + com.powsybl + powsybl-ws-commons + + + org.projectlombok + lombok + provided + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + org.apache.groovy + groovy + + + org.springframework.cloud + spring-cloud-stream + + + + + org.springframework.data + spring-data-elasticsearch + + + + + + + org.springframework.boot + spring-boot-starter-actuator + runtime + + + io.micrometer + micrometer-registry-prometheus + runtime + + + org.postgresql + postgresql + runtime + + + org.liquibase + liquibase-core + + + org.springframework.cloud + spring-cloud-stream-binder-rabbit + runtime + + + + + com.powsybl + powsybl-config-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.assertj + assertj-guava + test + + + com.h2database + h2 + test + + + com.vladmihalcea + db-util + test + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + elasticsearch + test + + + com.squareup.okhttp3 + mockwebserver3-junit5 + test + + + org.springframework.cloud + spring-cloud-stream-test-binder + test + + + org.wiremock + wiremock-jetty12 + test + + + diff --git a/src/main/java/org/gridsuite/modification/IFilterService.java b/src/main/java/org/gridsuite/modification/IFilterService.java new file mode 100644 index 0000000..6855950 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/IFilterService.java @@ -0,0 +1,19 @@ +package org.gridsuite.modification; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Stream; + +import org.gridsuite.filter.AbstractFilter; +import org.gridsuite.modification.dto.FilterEquipments; + +import com.powsybl.iidm.network.Network; + +public interface IFilterService { + List getFilters(List filtersUuids); + + Stream exportFilters(List filtersUuids, Network network); + + Map getUuidFilterEquipmentsMap(Network network, Map filters); +} diff --git a/src/main/java/org/gridsuite/modification/ModificationType.java b/src/main/java/org/gridsuite/modification/ModificationType.java new file mode 100644 index 0000000..efba552 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/ModificationType.java @@ -0,0 +1,84 @@ +/* + Copyright (c) 2021, All partners of the iTesla project (http://www.itesla-project.eu/consortium) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification; + +import com.powsybl.network.store.client.PreloadingStrategy; + +/** + * @author Slimane Amar + * @author Franck Lecuyer + */ +public enum ModificationType { + EQUIPMENT_ATTRIBUTE_MODIFICATION(PreloadingStrategy.NONE), + LOAD_CREATION(PreloadingStrategy.NONE), + LOAD_MODIFICATION(PreloadingStrategy.NONE), + BATTERY_CREATION(PreloadingStrategy.NONE), + BATTERY_MODIFICATION(PreloadingStrategy.NONE), + GENERATOR_CREATION(PreloadingStrategy.NONE), + GENERATOR_MODIFICATION(PreloadingStrategy.NONE), + EQUIPMENT_DELETION(PreloadingStrategy.NONE), + BY_FILTER_DELETION(PreloadingStrategy.COLLECTION), + LINE_CREATION(PreloadingStrategy.NONE), + LINE_MODIFICATION(PreloadingStrategy.NONE), + TWO_WINDINGS_TRANSFORMER_CREATION(PreloadingStrategy.NONE), + TWO_WINDINGS_TRANSFORMER_MODIFICATION(PreloadingStrategy.NONE), + GROOVY_SCRIPT(PreloadingStrategy.COLLECTION), + SUBSTATION_CREATION(PreloadingStrategy.NONE), + SUBSTATION_MODIFICATION(PreloadingStrategy.NONE), + SHUNT_COMPENSATOR_CREATION(PreloadingStrategy.NONE), + SHUNT_COMPENSATOR_MODIFICATION(PreloadingStrategy.NONE), + STATIC_VAR_COMPENSATOR_CREATION(PreloadingStrategy.NONE), + VOLTAGE_LEVEL_CREATION(PreloadingStrategy.NONE), + VOLTAGE_LEVEL_MODIFICATION(PreloadingStrategy.NONE), + LINE_SPLIT_WITH_VOLTAGE_LEVEL(PreloadingStrategy.NONE), + LINE_ATTACH_TO_VOLTAGE_LEVEL(PreloadingStrategy.NONE), + LINES_ATTACH_TO_SPLIT_LINES(PreloadingStrategy.NONE), + GENERATOR_SCALING(PreloadingStrategy.COLLECTION), + LOAD_SCALING(PreloadingStrategy.COLLECTION), + OPERATING_STATUS_MODIFICATION(PreloadingStrategy.NONE), + DELETE_VOLTAGE_LEVEL_ON_LINE(PreloadingStrategy.NONE), + DELETE_ATTACHING_LINE(PreloadingStrategy.NONE), + GENERATION_DISPATCH(PreloadingStrategy.COLLECTION), + VOLTAGE_INIT_MODIFICATION(PreloadingStrategy.ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW), + VSC_CREATION(PreloadingStrategy.NONE), + VSC_MODIFICATION(PreloadingStrategy.NONE), + CONVERTER_STATION_CREATION(PreloadingStrategy.NONE), + CONVERTER_STATION_MODIFICATION(PreloadingStrategy.NONE), + TABULAR_MODIFICATION(PreloadingStrategy.COLLECTION), + TABULAR_CREATION(PreloadingStrategy.COLLECTION), + BY_FORMULA_MODIFICATION(PreloadingStrategy.COLLECTION), + MODIFICATION_BY_ASSIGNMENT(PreloadingStrategy.COLLECTION), + COMPOSITE_MODIFICATION(PreloadingStrategy.COLLECTION); + + private final PreloadingStrategy strategy; + + ModificationType(PreloadingStrategy strategy) { + this.strategy = strategy; + } + + public PreloadingStrategy getStrategy() { + return strategy; + } + + public ModificationType maxStrategy(ModificationType other) { + return switch (strategy) { + case NONE -> { + if (other.strategy != PreloadingStrategy.NONE) { + yield other; + } + yield this; + } + case COLLECTION -> { + if (other.strategy == PreloadingStrategy.ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW) { + yield other; + } + yield this; + } + case ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW -> this; + }; + } +} diff --git a/src/main/java/org/gridsuite/modification/NetworkModificationException.java b/src/main/java/org/gridsuite/modification/NetworkModificationException.java new file mode 100644 index 0000000..3c0800d --- /dev/null +++ b/src/main/java/org/gridsuite/modification/NetworkModificationException.java @@ -0,0 +1,178 @@ +/* + Copyright (c) 2021, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.IdentifiableType; +import lombok.NonNull; + +import org.gridsuite.modification.dto.OperatingStatusModificationInfos; +import org.springframework.http.HttpStatus; + +import java.util.Objects; + +import static org.gridsuite.modification.NetworkModificationException.Type.ATTRIBUTE_NOT_EDITABLE; + +/** + * @author Slimane Amar + * @author Franck Lecuyer + */ +public class NetworkModificationException extends PowsyblException { + public enum Type { + GROOVY_SCRIPT_EMPTY(HttpStatus.BAD_REQUEST, "Empty script"), + GROOVY_SCRIPT_ERROR(HttpStatus.BAD_REQUEST), + NETWORK_NOT_FOUND(HttpStatus.NOT_FOUND), + VARIANT_NOT_FOUND(HttpStatus.NOT_FOUND), + NOTHING_TO_DELETE(HttpStatus.BAD_REQUEST), + MODIFICATION_DELETION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + MODIFICATION_GROUP_NOT_FOUND(HttpStatus.NOT_FOUND), + MODIFICATION_NOT_FOUND(HttpStatus.NOT_FOUND), + SWITCH_NOT_FOUND(HttpStatus.NOT_FOUND), + LINE_NOT_FOUND(HttpStatus.NOT_FOUND), + LOAD_NOT_FOUND(HttpStatus.NOT_FOUND), + BATTERY_NOT_FOUND(HttpStatus.NOT_FOUND), + GENERATOR_NOT_FOUND(HttpStatus.NOT_FOUND), + TWO_WINDINGS_TRANSFORMER_NOT_FOUND(HttpStatus.NOT_FOUND), + UNKNOWN_MODIFICATION_TYPE(HttpStatus.INTERNAL_SERVER_ERROR), + UNKNOWN_EQUIPMENT_TYPE(HttpStatus.INTERNAL_SERVER_ERROR), + WRONG_EQUIPMENT_TYPE(HttpStatus.INTERNAL_SERVER_ERROR), + MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + VOLTAGE_LEVEL_NOT_FOUND(HttpStatus.NOT_FOUND), + CREATE_LOAD_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + MODIFY_LOAD_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + BUSBAR_SECTION_NOT_FOUND(HttpStatus.NOT_FOUND), + BUS_NOT_FOUND(HttpStatus.NOT_FOUND), + CREATE_BATTERY_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + CREATE_GENERATOR_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + CREATE_SHUNT_COMPENSATOR_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + MODIFY_SHUNT_COMPENSATOR_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + CREATE_STATIC_VAR_COMPENSATOR_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + DELETE_EQUIPMENT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + BY_FILTER_DELETION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + EQUIPMENT_NOT_FOUND(HttpStatus.NOT_FOUND), + ATTRIBUTE_NOT_EDITABLE(HttpStatus.BAD_REQUEST), + CREATE_LINE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + MODIFY_LINE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + CREATE_TWO_WINDINGS_TRANSFORMER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + MODIFY_TWO_WINDINGS_TRANSFORMER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + CREATE_SUBSTATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + MODIFY_SUBSTATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + CREATE_VOLTAGE_LEVEL_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + MODIFY_VOLTAGE_LEVEL_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + SUBSTATION_NOT_FOUND(HttpStatus.NOT_FOUND), + BATTERY_ALREADY_EXISTS(HttpStatus.BAD_REQUEST), + LOAD_ALREADY_EXISTS(HttpStatus.BAD_REQUEST), + VOLTAGE_LEVEL_ALREADY_EXISTS(HttpStatus.BAD_REQUEST), + BUSBAR_SECTION_ALREADY_EXISTS(HttpStatus.BAD_REQUEST), + BUSBAR_SECTION_NOT_DEFINED(HttpStatus.BAD_REQUEST), + GENERATOR_ALREADY_EXISTS(HttpStatus.BAD_REQUEST), + SHUNT_COMPENSATOR_ALREADY_EXISTS(HttpStatus.BAD_REQUEST), + SHUNT_COMPENSATOR_NOT_FOUND(HttpStatus.NOT_FOUND), + STATIC_VAR_COMPENSATOR_ALREADY_EXISTS(HttpStatus.BAD_REQUEST), + STATIC_VAR_COMPENSATOR_NOT_FOUND(HttpStatus.NOT_FOUND), + LINE_ALREADY_EXISTS(HttpStatus.BAD_REQUEST), + TWO_WINDINGS_TRANSFORMER_ALREADY_EXISTS(HttpStatus.BAD_REQUEST), + TWO_WINDINGS_TRANSFORMER_CREATION_ERROR(HttpStatus.BAD_REQUEST), + BRANCH_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + INJECTION_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + CONNECTION_POSITION_ERROR(HttpStatus.BAD_REQUEST), + MODIFY_BATTERY_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + OPERATING_STATUS_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + OPERATING_ACTION_TYPE_EMPTY(HttpStatus.BAD_REQUEST, "Empty operating action type"), + OPERATING_ACTION_TYPE_UNKNOWN(HttpStatus.BAD_REQUEST), + OPERATING_ACTION_TYPE_UNSUPPORTED(HttpStatus.INTERNAL_SERVER_ERROR), + EQUIPMENT_TYPE_UNSUPPORTED(HttpStatus.INTERNAL_SERVER_ERROR), + LINE_SPLIT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + LINE_SPLIT_NOT_FOUND(HttpStatus.NOT_FOUND), + LINE_ATTACH_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + LINE_ATTACH_DESCRIPTION_ERROR(HttpStatus.BAD_REQUEST), + LINE_ATTACH_NOT_FOUND(HttpStatus.NOT_FOUND), + MODIFY_GENERATOR_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + TYPE_MISMATCH(HttpStatus.BAD_REQUEST), + MISSING_MODIFICATION_DESCRIPTION(HttpStatus.BAD_REQUEST), + MODIFICATION_OUT_OF_RANGE(HttpStatus.BAD_REQUEST), + POSITION_ORDER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + DELETE_VOLTAGE_LEVEL_ON_LINE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + DELETE_VOLTAGE_LEVEL_ON_LINE_NOT_FOUND(HttpStatus.NOT_FOUND), + EQUIPMENT_ATTRIBUTE_NAME_ERROR(HttpStatus.BAD_REQUEST), + EQUIPMENT_ATTRIBUTE_VALUE_ERROR(HttpStatus.BAD_REQUEST), + MOVE_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + GENERATOR_SCALING_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + LOAD_SCALING_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + DELETE_ATTACHING_LINE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + DELETE_ATTACHING_LINE_NOT_FOUND(HttpStatus.NOT_FOUND), + FILTERS_NOT_FOUND(HttpStatus.NOT_FOUND), + GENERATION_DISPATCH_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + VOLTAGE_INIT_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + TABULAR_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + TABULAR_CREATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + CREATE_VSC_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + MODIFY_VSC_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + HVDC_LINE_ALREADY_EXISTS(HttpStatus.BAD_REQUEST), + VSC_CONVERTER_STATION_NOT_FOUND(HttpStatus.NOT_FOUND), + CREATE_CONVERTER_STATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + MODIFY_CONVERTER_STATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + BY_FORMULA_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + MODIFICATION_BY_ASSIGNMENT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + HVDC_LINE_NOT_FOUND(HttpStatus.NOT_FOUND), + COMPOSITE_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + WRONG_HVDC_ANGLE_DROOP_ACTIVE_POWER_CONTROL(HttpStatus.BAD_REQUEST); + + public final HttpStatus status; + private final String message; + + HttpStatus getStatus() { + return status; + } + + Type(HttpStatus status) { + this(status, null); + } + + Type(HttpStatus status, String message) { + this.status = status; + this.message = message; + } + } + + private final Type type; + + public NetworkModificationException(Type type) { + super(Objects.requireNonNull(type.name()) + ((type.message == null) ? "" : " : " + type.message)); + this.type = type; + } + + public NetworkModificationException(Type type, Exception cause) { + super(Objects.requireNonNull(type.name()) + " : " + ((cause.getMessage() == null) ? cause.getClass().getName() : cause.getMessage()), cause); + this.type = type; + } + + public NetworkModificationException(Type type, String message) { + super(Objects.requireNonNull(type.name()) + " : " + Objects.requireNonNull(message)); + this.type = type; + } + + public Type getType() { + return type; + } + + public static NetworkModificationException createEquipmentTypeUnknown(@NonNull String type) { + return new NetworkModificationException(Type.UNKNOWN_EQUIPMENT_TYPE, "The equipment type : " + type + " is unknown"); + } + + public static NetworkModificationException createEquipmentTypeNotSupported(@NonNull String type) { + return new NetworkModificationException(Type.EQUIPMENT_TYPE_UNSUPPORTED, "The equipment type : " + type + " is not supported"); + } + + public static NetworkModificationException createOperatingActionTypeUnsupported(@NonNull OperatingStatusModificationInfos.ActionType type) { + return new NetworkModificationException(Type.OPERATING_ACTION_TYPE_UNSUPPORTED, "The operating action type : " + type + " is unsupported"); + } + + public static NetworkModificationException createEquipementAttributeNotEditable(@NonNull IdentifiableType equipmentType, @NonNull String attributeName) { + throw new NetworkModificationException(ATTRIBUTE_NOT_EDITABLE, equipmentType.name() + " attribute '" + attributeName + "' not editable"); + } +} diff --git a/src/main/java/org/gridsuite/modification/ReactiveVariationMode.java b/src/main/java/org/gridsuite/modification/ReactiveVariationMode.java new file mode 100644 index 0000000..cb9f348 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/ReactiveVariationMode.java @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification; + +/** + * @author bendaamerahm + */ +public enum ReactiveVariationMode { + CONSTANT_Q, + TAN_PHI_FIXED +} diff --git a/src/main/java/org/gridsuite/modification/TapChangerType.java b/src/main/java/org/gridsuite/modification/TapChangerType.java new file mode 100644 index 0000000..1efae67 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/TapChangerType.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification; +import java.util.Objects; + +/** + * @author Hugo Marcellin + */ +public enum TapChangerType { + RATIO("RATIO"), + PHASE("PHASE"); + + private final String description; + + TapChangerType(String description) { + this.description = Objects.requireNonNull(description); + } + + public String getDescription() { + return description; + } +} diff --git a/src/main/java/org/gridsuite/modification/VariationMode.java b/src/main/java/org/gridsuite/modification/VariationMode.java new file mode 100644 index 0000000..76200e7 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/VariationMode.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification; + +/** + * @author bendaamerahm + */ +public enum VariationMode { + PROPORTIONAL, + PROPORTIONAL_TO_PMAX, + REGULAR_DISTRIBUTION, + STACKING_UP, + VENTILATION +} diff --git a/src/main/java/org/gridsuite/modification/VariationType.java b/src/main/java/org/gridsuite/modification/VariationType.java new file mode 100644 index 0000000..73387b9 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/VariationType.java @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification; + +/** + * @author bendaamerahm + */ +public enum VariationType { + DELTA_P, + TARGET_P +} diff --git a/src/main/java/org/gridsuite/modification/dto/AbstractEquipmentDeletionInfos.java b/src/main/java/org/gridsuite/modification/dto/AbstractEquipmentDeletionInfos.java new file mode 100644 index 0000000..bfcc396 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/AbstractEquipmentDeletionInfos.java @@ -0,0 +1,31 @@ +/* + Copyright (c) 2023, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * @author David Braquart + */ + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "specificType" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = HvdcLccDeletionInfos.class, name = "HVDC_LINE_WITH_LCC") +}) + +@NoArgsConstructor +@Getter +@Setter +public abstract class AbstractEquipmentDeletionInfos { +} diff --git a/src/main/java/org/gridsuite/modification/dto/AttributeModification.java b/src/main/java/org/gridsuite/modification/dto/AttributeModification.java new file mode 100644 index 0000000..6f3c784 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/AttributeModification.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2022, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.util.Objects; + +/** + * @author Nicolas Noir + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Data +@ToString(callSuper = true) +@Schema(description = "Attribute modification") +public class AttributeModification { + private T value; + private OperationType op; + + public static AttributeModification toAttributeModification(V value, OperationType opType) { + if (Objects.isNull(value) && Objects.isNull(opType)) { + return null; + } else { + return new AttributeModification<>(value, opType); + } + } + + public T applyModification(T initialValue) { + if (op == OperationType.SET) { + return value; + } else if (op == OperationType.UNSET) { + return null; + } + // TODO throw exception + return initialValue; + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/BasicEquipmentModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/BasicEquipmentModificationInfos.java new file mode 100644 index 0000000..b935b64 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/BasicEquipmentModificationInfos.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author Nicolas Noir + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Basic equipment modification") +public class BasicEquipmentModificationInfos extends EquipmentModificationInfos { + @Schema(description = "Equipment name modification") + private AttributeModification equipmentName; +} diff --git a/src/main/java/org/gridsuite/modification/dto/BatteryCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/BatteryCreationInfos.java new file mode 100644 index 0000000..9d751db --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/BatteryCreationInfos.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.ModificationType; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.BatteryCreation; + +import java.util.List; + +/** + * @author Ghazwa Rehili + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Battery creation") +@JsonTypeName("BATTERY_CREATION") +@ModificationErrorTypeName("CREATE_BATTERY_ERROR") +public class BatteryCreationInfos extends InjectionCreationInfos implements ReactiveLimitsHolderInfos { + + @Schema(description = "Minimum active power") + private double minP; + + @Schema(description = "Maximum active power") + private double maxP; + + @Schema(description = "Minimum reactive power") + private Double minQ; + + @Schema(description = "Maximum reactive power") + private Double maxQ; + + @Schema(description = "Reactive capability curve points") + private List reactiveCapabilityCurvePoints; + + @Schema(description = "Active power set point") + private double targetP; + + @Schema(description = "Reactive power set point") + private Double targetQ; + + @Schema(description = "Participate") + private Boolean participate; + + @Schema(description = "Droop") + private Float droop; + + @Schema(description = "Reactive capability curve") + private Boolean reactiveCapabilityCurve; + + @Override + public AbstractModification toModification() { + return new BatteryCreation(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode().withMessageTemplate(ModificationType.BATTERY_CREATION.name(), "Battery creation ${batteryId}").withUntypedValue("batteryId", this.getEquipmentId()).add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/BatteryModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/BatteryModificationInfos.java new file mode 100644 index 0000000..c9d00b1 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/BatteryModificationInfos.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.BatteryModification; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author Ghazwa Rehili + */ + +@SuperBuilder +@NoArgsConstructor +@Data +@ToString(callSuper = true) +@Schema(description = "Battery modification") +@JsonTypeName("BATTERY_MODIFICATION") +@ModificationErrorTypeName("MODIFY_BATTERY_ERROR") +public class BatteryModificationInfos extends InjectionModificationInfos { + @Schema(description = "Minimum active power") + private AttributeModification minP; + + @Schema(description = "Maximum active power") + private AttributeModification maxP; + + @Schema(description = "Active power set point") + private AttributeModification targetP; + + @Schema(description = "Reactive power set point") + private AttributeModification targetQ; + + @Schema(description = "Participate") + private AttributeModification participate; + + @Schema(description = "Droop") + private AttributeModification droop; + + @Schema(description = "Minimum reactive power") + private AttributeModification minQ; + + @Schema(description = "Maximum reactive power") + private AttributeModification maxQ; + + @Schema(description = "Reactive capability curve points") + private List reactiveCapabilityCurvePoints; + + @Schema(description = "Reactive capability curve") + private AttributeModification reactiveCapabilityCurve; + + @Override + public AbstractModification toModification() { + return new BatteryModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode().withMessageTemplate(getType().name(), "Battery modification ${batteryId}").withUntypedValue("batteryId", this.getEquipmentId()).add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/BranchCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/BranchCreationInfos.java new file mode 100644 index 0000000..bb59e42 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/BranchCreationInfos.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2021, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author Sylvain Bouzols + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Branch creation") +public class BranchCreationInfos extends EquipmentCreationInfos { + + @Schema(description = "Series resistance") + private double r; + + @Schema(description = "Series reactance") + private double x; + + @Schema(description = "Voltage level id Side 1") + private String voltageLevelId1; + + @Schema(description = "Voltage level id Side 2") + private String voltageLevelId2; + + @Schema(description = "Bus or Busbar section id Side 1") + private String busOrBusbarSectionId1; + + @Schema(description = "Bus or Busbar section id Side 2") + private String busOrBusbarSectionId2; + + @Schema(description = "Current limits Side 1") + private CurrentLimitsInfos currentLimits1; + + @Schema(description = "Current limits Side 2") + private CurrentLimitsInfos currentLimits2; + + @Schema(description = "Connection Name 1") + private String connectionName1; + + @Schema(description = "Connection Direction 1") + private ConnectablePosition.Direction connectionDirection1; + + @Schema(description = "Connection Name 2") + private String connectionName2; + + @Schema(description = "Connection Direction 2") + private ConnectablePosition.Direction connectionDirection2; + + @Schema(description = "Connection position 1") + private Integer connectionPosition1; + + @Schema(description = "Connection position 2") + private Integer connectionPosition2; + + @Schema(description = "Connected 1") + private boolean connected1; + + @Schema(description = "Connected 2") + private boolean connected2; +} diff --git a/src/main/java/org/gridsuite/modification/dto/BranchModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/BranchModificationInfos.java new file mode 100644 index 0000000..6cd8fa2 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/BranchModificationInfos.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author Ayoub LABIDI + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Branch creation") +public class BranchModificationInfos extends BasicEquipmentModificationInfos { + + @Schema(description = "Series resistance") + private AttributeModification r; + + @Schema(description = "Series reactance") + private AttributeModification x; + + @Schema(description = "Current limits Side 1") + private CurrentLimitsModificationInfos currentLimits1; + + @Schema(description = "Current limits Side 2") + private CurrentLimitsModificationInfos currentLimits2; + + @Schema(description = "Voltage level id modification 1") + private AttributeModification voltageLevelId1; + + @Schema(description = "Voltage level id modification 2") + private AttributeModification voltageLevelId2; + + @Schema(description = "Bus id modification 1") + private AttributeModification busOrBusbarSectionId1; + + @Schema(description = "Bus id modification 2") + private AttributeModification busOrBusbarSectionId2; + + @Schema(description = "Connection Name 1") + private AttributeModification connectionName1; + + @Schema(description = "Connection Name 2") + private AttributeModification connectionName2; + + @Schema(description = "Connection Direction 1") + private AttributeModification connectionDirection1; + + @Schema(description = "Connection Direction 2") + private AttributeModification connectionDirection2; + + @Schema(description = "Connection Position 1") + private AttributeModification connectionPosition1; + + @Schema(description = "Connection Position 2") + private AttributeModification connectionPosition2; + + @Schema(description = "Connected 1") + private AttributeModification terminal1Connected; + + @Schema(description = "Connected 2") + private AttributeModification terminal2Connected; +} diff --git a/src/main/java/org/gridsuite/modification/dto/BusbarConnectionCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/BusbarConnectionCreationInfos.java new file mode 100644 index 0000000..d41cb00 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/BusbarConnectionCreationInfos.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.powsybl.iidm.network.SwitchKind; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +/** + * @author Laurent GARNIER + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Voltage level bus bar sections connection creation") +public class BusbarConnectionCreationInfos { + @Schema(description = "one side of the connection") + private String fromBBS; + + @Schema(description = "other side of the connection") + private String toBBS; + + @Schema(description = "switch on the connection") + private SwitchKind switchKind; +} diff --git a/src/main/java/org/gridsuite/modification/dto/BusbarSectionCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/BusbarSectionCreationInfos.java new file mode 100644 index 0000000..bde2ba5 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/BusbarSectionCreationInfos.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author Laurent GARNIER + */ +@ToString(callSuper = true) +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Voltage level bus bar section creation") +public class BusbarSectionCreationInfos { + @Schema(description = "bus bar section id") + private String id; + + @Schema(description = "bus bar section name") + private String name; + + @Schema(description = "vertical position") + private int vertPos; + + @Schema(description = "horizontal position") + private int horizPos; +} diff --git a/src/main/java/org/gridsuite/modification/dto/ByFilterDeletionInfos.java b/src/main/java/org/gridsuite/modification/dto/ByFilterDeletionInfos.java new file mode 100644 index 0000000..53a50b2 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ByFilterDeletionInfos.java @@ -0,0 +1,62 @@ +/* + Copyright (c) 2023, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.IdentifiableType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.ByFilterDeletion; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Antoine Bouhours + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "By filter deletion") +@JsonTypeName("BY_FILTER_DELETION") +@ModificationErrorTypeName("BY_FILTER_DELETION_ERROR") +public class ByFilterDeletionInfos extends ModificationInfos { + @Schema(description = "Equipment type") + private IdentifiableType equipmentType; + + @Schema(description = "List of filters") + @JsonInclude(JsonInclude.Include.NON_NULL) + private List filters; + + @Override + public AbstractModification toModification() { + return new ByFilterDeletion(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode().withMessageTemplate(getType().name(), "By filter deletion").add(); + } + + @Override + public Map getMapMessageValues() { + Map mapMessageValues = new HashMap<>(); + mapMessageValues.put("equipmentType", getEquipmentType().name()); + return mapMessageValues; + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/ByFormulaModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/ByFormulaModificationInfos.java new file mode 100644 index 0000000..8db5861 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ByFormulaModificationInfos.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.IdentifiableType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.ModificationType; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.dto.byfilter.formula.FormulaInfos; +import org.gridsuite.modification.modifications.byfilter.ByFormulaModification; + +import java.util.List; + +/** + * @author Seddik Yengui + */ + +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@JsonTypeName("BY_FORMULA_MODIFICATION") +@ModificationErrorTypeName("BY_FORMULA_MODIFICATION_ERROR") +@ToString(callSuper = true) +@Schema(description = "Modification by formula") +public class ByFormulaModificationInfos extends ModificationInfos { + @Schema(description = "Identifiable type") + private IdentifiableType identifiableType; + + @Schema(description = "list of formulas") + private List formulaInfosList; + + @Override + public ByFormulaModification toModification() { + return new ByFormulaModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode().withMessageTemplate(ModificationType.BY_FORMULA_MODIFICATION.name(), "Modification by formula").add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/CompositeModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/CompositeModificationInfos.java new file mode 100644 index 0000000..96ea483 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/CompositeModificationInfos.java @@ -0,0 +1,44 @@ +/* + Copyright (c) 2024, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.CompositeModification; + +import java.util.List; + +/** + * @author Ghazwa Rehili + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Composite modification") +@JsonTypeName("COMPOSITE_MODIFICATION") +@ModificationErrorTypeName("COMPOSITE_MODIFICATION_ERROR") +public class CompositeModificationInfos extends ModificationInfos { + + @Schema(description = "composite modification list") + @JsonInclude(JsonInclude.Include.NON_NULL) + private List modifications; + + @Override + public AbstractModification toModification() { + return new CompositeModification(this); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/ConverterStationCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/ConverterStationCreationInfos.java new file mode 100644 index 0000000..cb1cabc --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ConverterStationCreationInfos.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; + +import java.util.List; + +/** + * @author Seddik Yengui + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Converter station creation") +@JsonTypeName("CONVERTER_STATION_CREATION") +@ModificationErrorTypeName("CREATE_CONVERTER_STATION_ERROR") +public class ConverterStationCreationInfos extends InjectionCreationInfos implements ReactiveLimitsHolderInfos { + @Schema(description = "Loss Factor") + private Float lossFactor; + + @Schema(description = "Reactive power set point") + private Double reactivePowerSetpoint; + + @Schema(description = "Voltage regulation") + private Boolean voltageRegulationOn; + + @Schema(description = "Voltage set point") + private Double voltageSetpoint; + + @Schema(description = "Reactive capability curve") + private Boolean reactiveCapabilityCurve; + + @Schema(description = "Minimum reactive power") + private Double minQ; + + @Schema(description = "Maximum reactive power") + private Double maxQ; + + @Schema(description = "Reactive capability curve points") + private List reactiveCapabilityCurvePoints; + +} diff --git a/src/main/java/org/gridsuite/modification/dto/ConverterStationModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/ConverterStationModificationInfos.java new file mode 100644 index 0000000..6609bd8 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ConverterStationModificationInfos.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; + +import java.util.List; + +/** + * @author jamal kheyyad + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Converter station modification") +@JsonTypeName("CONVERTER_STATION_MODIFICATION") +@ModificationErrorTypeName("MODIFY_CONVERTER_STATION_ERROR") +public class ConverterStationModificationInfos extends InjectionModificationInfos { + @Schema(description = "Loss Factor") + private AttributeModification lossFactor; + + @Schema(description = "Reactive power set point ") + private AttributeModification reactivePowerSetpoint; + + @Schema(description = "Voltage regulation") + private AttributeModification voltageRegulationOn; + + @Schema(description = "Voltage set point") + private AttributeModification voltageSetpoint; + + @Schema(description = "Reactive capability curve") + private AttributeModification reactiveCapabilityCurve; + + @Schema(description = "Minimum reactive power") + private AttributeModification minQ; + + @Schema(description = "Maximum reactive power") + private AttributeModification maxQ; + + @Schema(description = "Reactive capability curve points") + private List reactiveCapabilityCurvePoints; + +} diff --git a/src/main/java/org/gridsuite/modification/dto/CouplingDeviceInfos.java b/src/main/java/org/gridsuite/modification/dto/CouplingDeviceInfos.java new file mode 100644 index 0000000..3e035aa --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/CouplingDeviceInfos.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +/** + * @author Ayoub LABIDI + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Coupling device creation") +public class CouplingDeviceInfos { + @Schema(description = "bus bar section 1 id") + private String busbarSectionId1; + + @Schema(description = "bus bar section 2 id") + private String busbarSectionId2; +} diff --git a/src/main/java/org/gridsuite/modification/dto/CurrentLimitsInfos.java b/src/main/java/org/gridsuite/modification/dto/CurrentLimitsInfos.java new file mode 100644 index 0000000..58e94ea --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/CurrentLimitsInfos.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2021, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author Sylvain Bouzols + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString +@Schema(description = "Current Limits") +public class CurrentLimitsInfos { + + @Schema(description = "Permanent current limit") + private Double permanentLimit; + + @Schema(description = "Temporary current limits") + private List temporaryLimits; + +} diff --git a/src/main/java/org/gridsuite/modification/dto/CurrentLimitsModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/CurrentLimitsModificationInfos.java new file mode 100644 index 0000000..7bfcaed --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/CurrentLimitsModificationInfos.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author Ayoub LABIDI + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Current Limits") +public class CurrentLimitsModificationInfos { + + @Schema(description = "Permanent current limit") + private Double permanentLimit; + + @Schema(description = "Temporary current limits") + private List temporaryLimits; + +} diff --git a/src/main/java/org/gridsuite/modification/dto/CurrentTemporaryLimitCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/CurrentTemporaryLimitCreationInfos.java new file mode 100644 index 0000000..3e954cf --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/CurrentTemporaryLimitCreationInfos.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +/** + * @author David Braquart + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CurrentTemporaryLimitCreationInfos { + + @Schema(description = "name") + private String name; + + @Schema(description = "value") + private Double value; + + @Schema(description = "acceptable duration") + private Integer acceptableDuration; +} diff --git a/src/main/java/org/gridsuite/modification/dto/CurrentTemporaryLimitModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/CurrentTemporaryLimitModificationInfos.java new file mode 100644 index 0000000..9fe7b63 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/CurrentTemporaryLimitModificationInfos.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +/** + * @author Ayoub LABIDI + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CurrentTemporaryLimitModificationInfos { + + @Schema(description = "name") + private String name; + + @Schema(description = "value") + private Double value; + + @Schema(description = "acceptable duration") + private Integer acceptableDuration; + + @Schema(description = "modification type") + private TemporaryLimitModificationType modificationType; +} diff --git a/src/main/java/org/gridsuite/modification/dto/DeleteAttachingLineInfos.java b/src/main/java/org/gridsuite/modification/dto/DeleteAttachingLineInfos.java new file mode 100644 index 0000000..b79cc13 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/DeleteAttachingLineInfos.java @@ -0,0 +1,72 @@ +/* + Copyright (c) 2022, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.DeleteAttachingLine; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author bendaamerahm + */ +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Delete attaching line infos") +@JsonTypeName("DELETE_ATTACHING_LINE") +@ModificationErrorTypeName("DELETE_ATTACHING_LINE_ERROR") +public class DeleteAttachingLineInfos extends ModificationInfos { + + @Schema(description = "line 1 id") + private String lineToAttachTo1Id; + + @Schema(description = "line 2 id") + private String lineToAttachTo2Id; + + @Schema(description = "attachment line id") + private String attachedLineId; + + @Schema(description = "replacing line 1 ID") + private String replacingLine1Id; + + @Schema(description = "replacing line 1 name") + private String replacingLine1Name; + + @Override + public AbstractModification toModification() { + return new DeleteAttachingLine(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode().withMessageTemplate(getType().name(), "Delete attaching line").add(); + } + + @Override + public Map getMapMessageValues() { + Map mapMessageValues = new HashMap<>(); + mapMessageValues.put("attachedLineId", getAttachedLineId()); + mapMessageValues.put("lineToAttachTo1Id", getLineToAttachTo1Id()); + mapMessageValues.put("lineToAttachTo2Id", getLineToAttachTo2Id()); + return mapMessageValues; + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/DeleteVoltageLevelOnLineInfos.java b/src/main/java/org/gridsuite/modification/dto/DeleteVoltageLevelOnLineInfos.java new file mode 100644 index 0000000..24fbeac --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/DeleteVoltageLevelOnLineInfos.java @@ -0,0 +1,66 @@ +/* + Copyright (c) 2022, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.DeleteVoltageLevelOnLine; + +import java.util.HashMap; +import java.util.Map; + + +/** + * @author bendaamerahm + */ +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Delete voltage level on line infos") +@JsonTypeName("DELETE_VOLTAGE_LEVEL_ON_LINE") +@ModificationErrorTypeName("DELETE_VOLTAGE_LEVEL_ON_LINE_ERROR") +public class DeleteVoltageLevelOnLineInfos extends ModificationInfos { + + @Schema(description = "line 1 id") + private String lineToAttachTo1Id; + + @Schema(description = "line 2 id") + private String lineToAttachTo2Id; + + @Schema(description = "replacing line 1 ID") + private String replacingLine1Id; + + @Schema(description = "replacing line 1 name") + private String replacingLine1Name; + + @Override + public AbstractModification toModification() { + return new DeleteVoltageLevelOnLine(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode().withMessageTemplate(getType().name(), "Delete voltage level on line").add(); + } + + @Override + public Map getMapMessageValues() { + Map mapMessageValues = new HashMap<>(); + mapMessageValues.put("lineToAttachTo1Id", getLineToAttachTo1Id()); + mapMessageValues.put("lineToAttachTo2Id", getLineToAttachTo2Id()); + return mapMessageValues; + } + +} diff --git a/src/main/java/org/gridsuite/modification/dto/EquipmentAttributeModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/EquipmentAttributeModificationInfos.java new file mode 100644 index 0000000..f3a3895 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/EquipmentAttributeModificationInfos.java @@ -0,0 +1,96 @@ +/* + Copyright (c) 2021, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.IdentifiableType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.EquipmentAttributeModification; +import org.springframework.lang.NonNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static org.gridsuite.modification.NetworkModificationException.Type.*; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Equipment attribute modification") +@JsonTypeName("EQUIPMENT_ATTRIBUTE_MODIFICATION") +@ModificationErrorTypeName("MODIFICATION_ERROR") +public class EquipmentAttributeModificationInfos extends EquipmentModificationInfos { + @Schema(description = "Equipment attribute name") + private String equipmentAttributeName; + + @Schema(description = "Equipment attribute value") + private Object equipmentAttributeValue; + + @Schema(description = "Equipment type") + @NonNull + private IdentifiableType equipmentType; + + @Override + public AbstractModification toModification() { + return new EquipmentAttributeModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "${EquipmentType} '${EquipmentId}' change") + .withTypedValue("EquipmentType", equipmentType.name(), TypedValue.UNTYPED) + .withTypedValue("EquipmentId", getEquipmentId(), TypedValue.UNTYPED) + .add(); + } + + @Override + public void check() { + super.check(); + if (equipmentType == IdentifiableType.SWITCH) { + checkSwitchStatusModificationInfos(); + } + } + + @Override + public Map getMapMessageValues() { + Map mapMessageValues = new HashMap<>(); + mapMessageValues.put("equipmentAttributeName", getEquipmentAttributeName()); + mapMessageValues.put("equipmentId", getEquipmentId()); + mapMessageValues.put("equipmentAttributeValue", getEquipmentAttributeValue() != null + ? getEquipmentAttributeValue().toString() + : null); + return mapMessageValues; + } + + private void checkSwitchStatusModificationInfos() { + if (!equipmentAttributeName.equals("open")) { + throw new NetworkModificationException(EQUIPMENT_ATTRIBUTE_NAME_ERROR, "For switch status, the attribute name is only 'open'"); + } + Set possibleValues = Set.of(true, false); + if (!possibleValues.contains(equipmentAttributeValue)) { + throw new NetworkModificationException(EQUIPMENT_ATTRIBUTE_VALUE_ERROR, "For switch status, the attribute values are only " + possibleValues); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/EquipmentCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/EquipmentCreationInfos.java new file mode 100644 index 0000000..ee232a8 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/EquipmentCreationInfos.java @@ -0,0 +1,28 @@ +/* + Copyright (c) 2021, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Equipment creation") +public class EquipmentCreationInfos extends EquipmentModificationInfos { + @Schema(description = "Equipment name") + private String equipmentName; +} diff --git a/src/main/java/org/gridsuite/modification/dto/EquipmentDeletionInfos.java b/src/main/java/org/gridsuite/modification/dto/EquipmentDeletionInfos.java new file mode 100644 index 0000000..9492178 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/EquipmentDeletionInfos.java @@ -0,0 +1,55 @@ +/* + Copyright (c) 2021, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.IdentifiableType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.EquipmentDeletion; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Equipment deletion") +@JsonTypeName("EQUIPMENT_DELETION") +@ModificationErrorTypeName("DELETE_EQUIPMENT_ERROR") +public class EquipmentDeletionInfos extends EquipmentModificationInfos { + @Schema(description = "Equipment type") + private IdentifiableType equipmentType; + + @Schema(description = "Equipment specific infos (optional)") + @JsonInclude(JsonInclude.Include.NON_NULL) + private AbstractEquipmentDeletionInfos equipmentInfos; + + @Override + public AbstractModification toModification() { + return new EquipmentDeletion(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Equipment deletion ${equipmentId}") + .withUntypedValue("equipmentId", this.getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/EquipmentModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/EquipmentModificationInfos.java new file mode 100644 index 0000000..ee30c53 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/EquipmentModificationInfos.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2021, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import org.springframework.lang.NonNull; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import java.util.List; +import java.util.Map; + +/** + * @author Slimane Amar + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Equipment modification") +public class EquipmentModificationInfos extends ModificationInfos { + @Schema(description = "Equipment ID") + @NonNull + private String equipmentId; + + @Schema(description = "free properties") + @JsonInclude(JsonInclude.Include.NON_NULL) + private List properties; + + @Override + public Map getMapMessageValues() { + return Map.of("equipmentId", getEquipmentId()); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/FilterEquipments.java b/src/main/java/org/gridsuite/modification/dto/FilterEquipments.java new file mode 100644 index 0000000..64ac68b --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/FilterEquipments.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; +import java.util.UUID; + +/** + * @author bendaamerahm + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class FilterEquipments { + @Schema(description = "filter id") + private UUID filterId; + + @Schema(description = "filter name") + private String filterName; + + @Schema(description = "equipments of filter") + private List identifiableAttributes; + + @Schema(description = "equipments not found in network") + private List notFoundEquipments; +} diff --git a/src/main/java/org/gridsuite/modification/dto/FilterInfos.java b/src/main/java/org/gridsuite/modification/dto/FilterInfos.java new file mode 100644 index 0000000..1864e2c --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/FilterInfos.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.util.UUID; + +/** + * @author bendaamerahm + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@Schema(description = "Filter Infos") +public class FilterInfos { + + @Schema(description = "id of filter") + private UUID id; + + @Schema(description = "name of filter") + private String name; + +} diff --git a/src/main/java/org/gridsuite/modification/dto/FreePropertyInfos.java b/src/main/java/org/gridsuite/modification/dto/FreePropertyInfos.java new file mode 100644 index 0000000..629bb87 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/FreePropertyInfos.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; + +/** + * @author Joris Mancini + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@EqualsAndHashCode +@JsonInclude(JsonInclude.Include.NON_NULL) +@Schema(description = "Free properties") +public class FreePropertyInfos { + @Schema(description = "property name") + private String name; + + @Schema(description = "property value") + private String value; + + @Schema(description = "marked as deleted") + private boolean deletionMark = false; + + @Schema(description = "property added in current modification") + private boolean added = false; + + @Schema(description = "previous value") + private String previousValue; + +} diff --git a/src/main/java/org/gridsuite/modification/dto/GenerationDispatchInfos.java b/src/main/java/org/gridsuite/modification/dto/GenerationDispatchInfos.java new file mode 100644 index 0000000..4fed34b --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/GenerationDispatchInfos.java @@ -0,0 +1,63 @@ +/* + Copyright (c) 2023, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.GenerationDispatch; + +import java.util.List; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@Schema(description = "Generation dispatch creation") +@JsonTypeName("GENERATION_DISPATCH") +@ModificationErrorTypeName("GENERATION_DISPATCH_ERROR") +public class GenerationDispatchInfos extends ModificationInfos { + @Schema(description = "loss coefficient") + private Double lossCoefficient; + + @Schema(description = "default outage rate") + private Double defaultOutageRate; + + @Schema(description = "generators without outage") + private List generatorsWithoutOutage; + + @Schema(description = "generators with fixed supply") + private List generatorsWithFixedSupply; + + @Schema(description = "generators frequency reserve") + private List generatorsFrequencyReserve; + + @Schema(description = "substations hierarchy for ordering generators with marginal cost") + private List substationsGeneratorsOrdering; + + @Override + public AbstractModification toModification() { + return new GenerationDispatch(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Generation dispatch") + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/GeneratorCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/GeneratorCreationInfos.java new file mode 100644 index 0000000..f09eabb --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/GeneratorCreationInfos.java @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2020, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.EnergySource; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.GeneratorCreation; + +import java.util.List; + +/** + * @author Franck Lecuyer + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Generator creation") +@JsonTypeName("GENERATOR_CREATION") +@ModificationErrorTypeName("CREATE_GENERATOR_ERROR") +public class GeneratorCreationInfos extends InjectionCreationInfos implements ReactiveLimitsHolderInfos { + @Schema(description = "Energy source") + private EnergySource energySource; + + @Schema(description = "Minimum active power") + private double minP; + + @Schema(description = "Maximum active power") + private double maxP; + + @Schema(description = "Rated nominal power") + private Double ratedS; + + @Schema(description = "Active power set point") + private double targetP; + + @Schema(description = "Reactive power set point") + private Double targetQ; + + @Schema(description = "Voltage regulation on") + private boolean voltageRegulationOn; + + @Schema(description = "Voltage set point") + private Double targetV; + + @Schema(description = "Planning active power set point") + private Double plannedActivePowerSetPoint; + + @Schema(description = "Marginal cost") + private Double marginalCost; + + @Schema(description = "Planning outage rate") + private Double plannedOutageRate; + + @Schema(description = "Forced outage rate") + private Double forcedOutageRate; + + @Schema(description = "Minimum reactive power") + private Double minQ; + + @Schema(description = "Maximum reactive power") + private Double maxQ; + + @Schema(description = "Reactive capability curve points") + private List reactiveCapabilityCurvePoints; + + @Schema(description = "Participate") + private Boolean participate; + + @Schema(description = "Droop") + private Float droop; + + @Schema(description = "Transient reactance") + private Double directTransX; + + @Schema(description = "Step up transformer reactance") + private Double stepUpTransformerX; + + @Schema(description = "Regulating terminal equipment id") + private String regulatingTerminalId; + + @Schema(description = "Regulating terminal equipment type") + private String regulatingTerminalType; + + @Schema(description = "Regulating terminal voltage level id") + private String regulatingTerminalVlId; + + // As this attribute has only one lower case letter at its start (xXXXX), the getters is parsed as getQPercent and the field for Jackson is parsed as qpercent + // while we expect qPercent. JsonProperty let fix the json field to qPercent + @JsonProperty("qPercent") + @Schema(description = "Q percent") + private Double qPercent; + + @Schema(description = "Reactive capability curve") + private Boolean reactiveCapabilityCurve; + + @Override + public AbstractModification toModification() { + return new GeneratorCreation(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Generator creation ${generatorId}") + .withUntypedValue("generatorId", this.getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/GeneratorModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/GeneratorModificationInfos.java new file mode 100644 index 0000000..a6fc384 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/GeneratorModificationInfos.java @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.GeneratorModification; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.powsybl.iidm.network.EnergySource; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author Jacques Borsenberger + */ + +@SuperBuilder +@NoArgsConstructor +@Data +@ToString(callSuper = true) +@Schema(description = "generator modification") +@JsonTypeName("GENERATOR_MODIFICATION") +@ModificationErrorTypeName("MODIFY_GENERATOR_ERROR") +public class GeneratorModificationInfos extends InjectionModificationInfos { + @Schema(description = "Energy source") + private AttributeModification energySource; + + @Schema(description = "Minimum active power") + private AttributeModification minP; + + @Schema(description = "Maximum active power") + private AttributeModification maxP; + + @Schema(description = "Rated nominal power") + private AttributeModification ratedS; + + @Schema(description = "Active power set point") + private AttributeModification targetP; + + @Schema(description = "Reactive power set point") + private AttributeModification targetQ; + + @Schema(description = "Voltage regulation on") + private AttributeModification voltageRegulationOn; + + @Schema(description = "Voltage set point") + private AttributeModification targetV; + + @Schema(description = "Planning active power set point") + private AttributeModification plannedActivePowerSetPoint; + + @Schema(description = "Marginal cost") + private AttributeModification marginalCost; + + @Schema(description = "Planning outage rate") + private AttributeModification plannedOutageRate; + + @Schema(description = "Forced outage rate") + private AttributeModification forcedOutageRate; + + @Schema(description = "Minimum reactive power") + private AttributeModification minQ; + + @Schema(description = "Maximum reactive power") + private AttributeModification maxQ; + + @Schema(description = "Reactive capability curve points") + private List reactiveCapabilityCurvePoints; + + @Schema(description = "Participate") + private AttributeModification participate; + + @Schema(description = "Droop") + private AttributeModification droop; + + @Schema(description = "Transient reactance") + private AttributeModification directTransX; + + @Schema(description = "Step up transformer reactance") + private AttributeModification stepUpTransformerX; + + @Schema(description = "Voltage Regulation type") + private AttributeModification voltageRegulationType; + + @Schema(description = "Regulating terminal equipment id") + private AttributeModification regulatingTerminalId; + + @Schema(description = "Regulating terminal equipment type") + private AttributeModification regulatingTerminalType; + + @Schema(description = "Regulating terminal voltage level id") + private AttributeModification regulatingTerminalVlId; + + // As this attribute has only one lower case letter at its start (xXXXX), the getters is parsed as getQPercent and the field for Jackson is parsed as qpercent + // while we expect qPercent. JsonProperty let fix the json field to qPercent + @JsonProperty("qPercent") + @Schema(description = "Q percent") + private AttributeModification qPercent; + + @Schema(description = "Reactive capability curve") + private AttributeModification reactiveCapabilityCurve; + + @Override + public AbstractModification toModification() { + return new GeneratorModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Generator modification ${generatorId}") + .withUntypedValue("generatorId", this.getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/GeneratorScalingInfos.java b/src/main/java/org/gridsuite/modification/dto/GeneratorScalingInfos.java new file mode 100644 index 0000000..73eb26a --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/GeneratorScalingInfos.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.GeneratorScaling; + +/** + * @author Seddik Yengui + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@JsonTypeName("GENERATOR_SCALING") +@ModificationErrorTypeName("GENERATOR_SCALING_ERROR") +@Schema(description = "Generator scaling creation") +public class GeneratorScalingInfos extends ScalingInfos { + + @Override + public AbstractModification toModification() { + return new GeneratorScaling(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Generator scaling") + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/GeneratorsFilterInfos.java b/src/main/java/org/gridsuite/modification/dto/GeneratorsFilterInfos.java new file mode 100644 index 0000000..5fe779b --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/GeneratorsFilterInfos.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +import java.util.UUID; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Generators filter Infos") +public class GeneratorsFilterInfos { + + @Schema(description = "id of generators filter") + private UUID id; + + @Schema(description = "name of generators filter") + private String name; +} diff --git a/src/main/java/org/gridsuite/modification/dto/GeneratorsFrequencyReserveInfos.java b/src/main/java/org/gridsuite/modification/dto/GeneratorsFrequencyReserveInfos.java new file mode 100644 index 0000000..121a068 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/GeneratorsFrequencyReserveInfos.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Generators frequency reserve infos") +public class GeneratorsFrequencyReserveInfos { + @Schema(description = "generators filters") + private List generatorsFilters; + + @Schema(description = "frequency reserve") + private Double frequencyReserve; +} diff --git a/src/main/java/org/gridsuite/modification/dto/GeneratorsWithoutOutageInfos.java b/src/main/java/org/gridsuite/modification/dto/GeneratorsWithoutOutageInfos.java new file mode 100644 index 0000000..1cc6ac3 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/GeneratorsWithoutOutageInfos.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +import java.util.UUID; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Filter Infos") +public class GeneratorsWithoutOutageInfos { + + @Schema(description = "id of generators filter") + private UUID id; + + @Schema(description = "name of generators filter") + private String name; +} diff --git a/src/main/java/org/gridsuite/modification/dto/GroovyScriptInfos.java b/src/main/java/org/gridsuite/modification/dto/GroovyScriptInfos.java new file mode 100644 index 0000000..9986e21 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/GroovyScriptInfos.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2021, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.GroovyScript; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Groovy script modification") +@JsonTypeName("GROOVY_SCRIPT") +@ModificationErrorTypeName("GROOVY_SCRIPT_ERROR") +public class GroovyScriptInfos extends ModificationInfos { + @Schema(description = "Groovy script") + private String script; + + @Override + public AbstractModification toModification() { + return new GroovyScript(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Apply groovy script") + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/HvdcLccDeletionInfos.java b/src/main/java/org/gridsuite/modification/dto/HvdcLccDeletionInfos.java new file mode 100644 index 0000000..a6cd817 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/HvdcLccDeletionInfos.java @@ -0,0 +1,43 @@ +/* + Copyright (c) 2023, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.util.List; + +/** + * @author David Braquart + */ + +@NoArgsConstructor +@Getter +@Setter +@Schema(description = "Hvdc with Lcc deletion") +public class HvdcLccDeletionInfos extends AbstractEquipmentDeletionInfos { + + @Builder + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class ShuntCompensatorInfos { + private String id; + private boolean connectedToHvdc; + } + + @Schema(description = "LCC HVDC converter station Shunt Compensator side 1") + @JsonInclude(JsonInclude.Include.NON_NULL) + private List mcsOnSide1; + + @Schema(description = "LCC HVDC converter station Shunt Compensator side 2") + @JsonInclude(JsonInclude.Include.NON_NULL) + private List mcsOnSide2; + +} diff --git a/src/main/java/org/gridsuite/modification/dto/IdentifiableAttributes.java b/src/main/java/org/gridsuite/modification/dto/IdentifiableAttributes.java new file mode 100644 index 0000000..edcb4ee --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/IdentifiableAttributes.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.powsybl.iidm.network.IdentifiableType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Schema(description = "Identifiable attributes") +public class IdentifiableAttributes { + + @Schema(description = "identifiable id") + private String id; + + @Schema(description = "identifiable type") + private IdentifiableType type; + + @Schema(description = "distribution key") + private Double distributionKey; +} diff --git a/src/main/java/org/gridsuite/modification/dto/InjectionCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/InjectionCreationInfos.java new file mode 100644 index 0000000..749b668 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/InjectionCreationInfos.java @@ -0,0 +1,44 @@ +/* + Copyright (c) 2021, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Injection creation") +public class InjectionCreationInfos extends EquipmentCreationInfos { + @Schema(description = "Voltage level id") + private String voltageLevelId; + + @Schema(description = "Bus id") + private String busOrBusbarSectionId; + + @Schema(description = "Connection Name") + private String connectionName; + + @Schema(description = "Connection Direction") + private ConnectablePosition.Direction connectionDirection; + + @Schema(description = "Connection Position") + private Integer connectionPosition; + + @Schema(description = "Connected") + private boolean terminalConnected; +} diff --git a/src/main/java/org/gridsuite/modification/dto/InjectionModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/InjectionModificationInfos.java new file mode 100644 index 0000000..0fe7a67 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/InjectionModificationInfos.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author Nicolas Noir + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Injection modification") +public class InjectionModificationInfos extends BasicEquipmentModificationInfos { + @Schema(description = "Voltage level id modification") + private AttributeModification voltageLevelId; + + @Schema(description = "Bus id modification") + private AttributeModification busOrBusbarSectionId; + + @Schema(description = "Connection Name") + private AttributeModification connectionName; + + @Schema(description = "Connection Direction") + private AttributeModification connectionDirection; + + @Schema(description = "Connection Position") + private AttributeModification connectionPosition; + + @Schema(description = "Connected") + private AttributeModification terminalConnected; +} diff --git a/src/main/java/org/gridsuite/modification/dto/LineAttachToVoltageLevelInfos.java b/src/main/java/org/gridsuite/modification/dto/LineAttachToVoltageLevelInfos.java new file mode 100644 index 0000000..8e2ad78 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/LineAttachToVoltageLevelInfos.java @@ -0,0 +1,83 @@ +/* + Copyright (c) 2022, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.LineAttachToVoltageLevel; +import java.util.Map; + +/** + * @author Nicolas NOIR + */ +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Line attach to voltage level") +@JsonTypeName("LINE_ATTACH_TO_VOLTAGE_LEVEL") +@ModificationErrorTypeName("LINE_ATTACH_ERROR") +public class LineAttachToVoltageLevelInfos extends ModificationInfos { + + @Schema(description = "line to attach to ID") + private String lineToAttachToId; + + @Schema(description = "percentage of line length from side 1") + private double percent; + + @Schema(description = "attachment point id") + private String attachmentPointId; + + @Schema(description = "attachment point name") + private String attachmentPointName; + + @Schema(description = "possible new voltage level to create before inserting it, may be null") + private VoltageLevelCreationInfos mayNewVoltageLevelInfos; + + @Schema(description = "if no new voltage level, ID for the existing voltage level") + private String existingVoltageLevelId; + + @Schema(description = "bus bar section or bus id") + private String bbsOrBusId; + + @Schema(description = "attachment line") + private LineCreationInfos attachmentLine; + + @Schema(description = "new line 1 ID") + private String newLine1Id; + + @Schema(description = "new line 1 name") + private String newLine1Name; + + @Schema(description = "new line 1 ID") + private String newLine2Id; + + @Schema(description = "new line 2 name") + private String newLine2Name; + + @Override + public AbstractModification toModification() { + return new LineAttachToVoltageLevel(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode().withMessageTemplate(getType().name(), "Line attach to voltage level").add(); + } + + @Override + public Map getMapMessageValues() { + return Map.of("lineToAttachToId", getLineToAttachToId()); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/LineCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/LineCreationInfos.java new file mode 100644 index 0000000..e272fa0 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/LineCreationInfos.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2021, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.LineCreation; + +/** + * @author Sylvain Bouzols + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Line creation") +@JsonTypeName("LINE_CREATION") +@ModificationErrorTypeName("CREATE_LINE_ERROR") +public class LineCreationInfos extends BranchCreationInfos { + + @Schema(description = "Shunt conductance Side 1") + private Double g1; + + @Schema(description = "Shunt susceptance Side 1") + private Double b1; + + @Schema(description = "Shunt conductance Side 2") + private Double g2; + + @Schema(description = "Shunt susceptance Side 2") + private Double b2; + + @Override + public AbstractModification toModification() { + return new LineCreation(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Creation of line ${lineId}") + .withUntypedValue("lineId", getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/LineModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/LineModificationInfos.java new file mode 100644 index 0000000..6a1f97c --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/LineModificationInfos.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.LineModification; + +/** + * @author Ayoub LABIDI + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Line modification") +@JsonTypeName("LINE_MODIFICATION") +@ModificationErrorTypeName("MODIFY_LINE_ERROR") +public class LineModificationInfos extends BranchModificationInfos { + + @Schema(description = "Shunt conductance Side 1") + private AttributeModification g1; + + @Schema(description = "Shunt susceptance Side 1") + private AttributeModification b1; + + @Schema(description = "Shunt conductance Side 2") + private AttributeModification g2; + + @Schema(description = "Shunt susceptance Side 2") + private AttributeModification b2; + + @Override + public AbstractModification toModification() { + return new LineModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Line modification ${lineId}") + .withUntypedValue("lineId", getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/LineSplitWithVoltageLevelInfos.java b/src/main/java/org/gridsuite/modification/dto/LineSplitWithVoltageLevelInfos.java new file mode 100644 index 0000000..16d1c2a --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/LineSplitWithVoltageLevelInfos.java @@ -0,0 +1,78 @@ +/* + Copyright (c) 2022, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.LineSplitWithVoltageLevel; + +import java.util.Map; + +/** + * @author Laurent GARNIER + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Line split with voltage level") +@JsonTypeName("LINE_SPLIT_WITH_VOLTAGE_LEVEL") +@ModificationErrorTypeName("LINE_SPLIT_ERROR") +public class LineSplitWithVoltageLevelInfos extends ModificationInfos { + + @Schema(description = "line to split ID") + private String lineToSplitId; + + @Schema(description = "percentage of line length from side 1") + private double percent; + + @Schema(description = "possible new voltage level to create before inserting it, may be null") + private VoltageLevelCreationInfos mayNewVoltageLevelInfos; + + @Schema(description = "if no new voltage level, ID for the existing voltage level") + private String existingVoltageLevelId; + + @Schema(description = "bus bar section or bus id") + private String bbsOrBusId; + + @Schema(description = "new line 1 ID") + private String newLine1Id; + + @Schema(description = "new line 1 name") + private String newLine1Name; + + @Schema(description = "new line 1 ID") + private String newLine2Id; + + @Schema(description = "new line 2 name") + private String newLine2Name; + + @Override + public AbstractModification toModification() { + return new LineSplitWithVoltageLevel(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Line split with voltage level") + .add(); + } + + @Override + public Map getMapMessageValues() { + return Map.of("lineToSplitId", getLineToSplitId()); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/LinesAttachToSplitLinesInfos.java b/src/main/java/org/gridsuite/modification/dto/LinesAttachToSplitLinesInfos.java new file mode 100644 index 0000000..6b8bfa0 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/LinesAttachToSplitLinesInfos.java @@ -0,0 +1,77 @@ +/* + Copyright (c) 2022, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.LinesAttachToSplitLines; +import java.util.Map; + + +/** + * @author bendaamerahm + */ +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Line attach to split line") +@JsonTypeName("LINES_ATTACH_TO_SPLIT_LINES") +@ModificationErrorTypeName("LINE_ATTACH_ERROR") +public class LinesAttachToSplitLinesInfos extends ModificationInfos { + + @Schema(description = "line 1 id") + private String lineToAttachTo1Id; + + @Schema(description = "line 2 id") + private String lineToAttachTo2Id; + + @Schema(description = "attachment line id") + private String attachedLineId; + + @Schema(description = "ID for the existing voltage level") + private String voltageLevelId; + + @Schema(description = "bus bar section or bus id") + private String bbsBusId; + + @Schema(description = "replacing line 1 ID") + private String replacingLine1Id; + + @Schema(description = "replacing line 1 name") + private String replacingLine1Name; + + @Schema(description = "replacing line 1 ID") + private String replacingLine2Id; + + @Schema(description = "replacing line 2 name") + private String replacingLine2Name; + + @Override + public AbstractModification toModification() { + return new LinesAttachToSplitLines(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Lines attach to split lines") + .add(); + } + + @Override + public Map getMapMessageValues() { + return Map.of("attachedLineId", getAttachedLineId()); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/LoadCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/LoadCreationInfos.java new file mode 100644 index 0000000..439c74c --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/LoadCreationInfos.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2020, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.LoadType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.LoadCreation; + +/** + * @author Franck Lecuyer + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Load creation") +@JsonTypeName("LOAD_CREATION") +@ModificationErrorTypeName("CREATE_LOAD_ERROR") +public class LoadCreationInfos extends InjectionCreationInfos { + @Schema(description = "Load type") + private LoadType loadType; + + @Schema(description = "Active power") + private double p0; + + @Schema(description = "Reactive power") + private double q0; + + @Override + public AbstractModification toModification() { + return new LoadCreation(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Load creation ${loadId}") + .withUntypedValue("loadId", getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/LoadModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/LoadModificationInfos.java new file mode 100644 index 0000000..d866f77 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/LoadModificationInfos.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.LoadModification; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.powsybl.iidm.network.LoadType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@Schema(description = "Load modification") +@JsonTypeName("LOAD_MODIFICATION") +@ModificationErrorTypeName("MODIFY_LOAD_ERROR") +public class LoadModificationInfos extends InjectionModificationInfos { + @Schema(description = "Load type modification") + private AttributeModification loadType; + + @Schema(description = "Active power modification") + private AttributeModification p0; + + @Schema(description = "Reactive power modification") + private AttributeModification q0; + + @Override + public AbstractModification toModification() { + return new LoadModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Load modification ${loadId}") + .withUntypedValue("loadId", getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/LoadScalingInfos.java b/src/main/java/org/gridsuite/modification/dto/LoadScalingInfos.java new file mode 100644 index 0000000..7360fad --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/LoadScalingInfos.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.LoadScaling; + +/** + * @author bendaamerahm + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Load scaling creation") +@JsonTypeName("LOAD_SCALING") +@ModificationErrorTypeName("LOAD_SCALING_ERROR") +public class LoadScalingInfos extends ScalingInfos { + + @Override + public AbstractModification toModification() { + return new LoadScaling(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Load Scaling") + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/ModificationByAssignmentInfos.java b/src/main/java/org/gridsuite/modification/dto/ModificationByAssignmentInfos.java new file mode 100644 index 0000000..5e2f7c4 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ModificationByAssignmentInfos.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.IdentifiableType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.ModificationType; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.dto.byfilter.assignment.AssignmentInfos; +import org.gridsuite.modification.modifications.byfilter.ModificationByAssignment; + +import java.util.List; + +/** + * @author Thang PHAM + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@JsonTypeName("MODIFICATION_BY_ASSIGNMENT") +@ModificationErrorTypeName("MODIFICATION_BY_ASSIGNMENT_ERROR") +@ToString(callSuper = true) +@Schema(description = "Modification by assignment") +public class ModificationByAssignmentInfos extends ModificationInfos { + @Schema(description = "Equipment type") + private IdentifiableType equipmentType; + + @Schema(description = "list of modifications") + private List> assignmentInfosList; + + @Override + public ModificationByAssignment toModification() { + return new ModificationByAssignment(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode().withMessageTemplate(ModificationType.MODIFICATION_BY_ASSIGNMENT.name(), "Modification by filter").add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/ModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/ModificationInfos.java new file mode 100644 index 0000000..3a56daf --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ModificationInfos.java @@ -0,0 +1,141 @@ +/* + Copyright (c) 2021, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.time.Instant; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.ModificationType; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.modifications.AbstractModification; + +/** + * @author Slimane Amar + */ +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "type", + include = JsonTypeInfo.As.EXISTING_PROPERTY +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = GroovyScriptInfos.class), + @JsonSubTypes.Type(value = BatteryCreationInfos.class), + @JsonSubTypes.Type(value = BatteryModificationInfos.class), + @JsonSubTypes.Type(value = LoadCreationInfos.class), + @JsonSubTypes.Type(value = LoadModificationInfos.class), + @JsonSubTypes.Type(value = GeneratorCreationInfos.class), + @JsonSubTypes.Type(value = GeneratorModificationInfos.class), + @JsonSubTypes.Type(value = LineCreationInfos.class), + @JsonSubTypes.Type(value = LineModificationInfos.class), + @JsonSubTypes.Type(value = SubstationCreationInfos.class), + @JsonSubTypes.Type(value = SubstationModificationInfos.class), + @JsonSubTypes.Type(value = VoltageLevelCreationInfos.class), + @JsonSubTypes.Type(value = VoltageLevelModificationInfos.class), + @JsonSubTypes.Type(value = ShuntCompensatorCreationInfos.class), + @JsonSubTypes.Type(value = ShuntCompensatorModificationInfos.class), + @JsonSubTypes.Type(value = StaticVarCompensatorCreationInfos.class), + @JsonSubTypes.Type(value = TwoWindingsTransformerCreationInfos.class), + @JsonSubTypes.Type(value = TwoWindingsTransformerModificationInfos.class), + @JsonSubTypes.Type(value = EquipmentDeletionInfos.class), + @JsonSubTypes.Type(value = ByFilterDeletionInfos.class), + @JsonSubTypes.Type(value = LineSplitWithVoltageLevelInfos.class), + @JsonSubTypes.Type(value = LineAttachToVoltageLevelInfos.class), + @JsonSubTypes.Type(value = LinesAttachToSplitLinesInfos.class), + @JsonSubTypes.Type(value = OperatingStatusModificationInfos.class), + @JsonSubTypes.Type(value = EquipmentAttributeModificationInfos.class), + @JsonSubTypes.Type(value = GeneratorScalingInfos.class), + @JsonSubTypes.Type(value = LoadScalingInfos.class), + @JsonSubTypes.Type(value = DeleteVoltageLevelOnLineInfos.class), + @JsonSubTypes.Type(value = DeleteAttachingLineInfos.class), + @JsonSubTypes.Type(value = GenerationDispatchInfos.class), + @JsonSubTypes.Type(value = VoltageInitModificationInfos.class), + @JsonSubTypes.Type(value = VscCreationInfos.class), + @JsonSubTypes.Type(value = ConverterStationCreationInfos.class), + @JsonSubTypes.Type(value = TabularModificationInfos.class), + @JsonSubTypes.Type(value = ByFormulaModificationInfos.class), + @JsonSubTypes.Type(value = ModificationByAssignmentInfos.class), + @JsonSubTypes.Type(value = VscModificationInfos.class), + @JsonSubTypes.Type(value = ConverterStationModificationInfos.class), + @JsonSubTypes.Type(value = TabularCreationInfos.class), + @JsonSubTypes.Type(value = CompositeModificationInfos.class) +}) +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString +@Schema(description = "Modification attributes") +public class ModificationInfos { + @Schema(description = "Modification id") + private UUID uuid; + + @Schema(description = "Modification type") + @Setter(AccessLevel.NONE) + private final AtomicReference type = new AtomicReference<>(null); // Only accessor (automatically initialized) + + @Schema(description = "Modification date") + private Instant date; + + @Schema(description = "Modification flag") + @Builder.Default + private Boolean stashed = false; + + @Schema(description = "Message type") + private String messageType; + + @Schema(description = "Message values") + private String messageValues; + + @Schema(description = "Modification activated") + @Builder.Default + private Boolean activated = true; + + @JsonIgnore + public ReportNode createSubReportNode(ReportNode reportNode) { + throw new UnsupportedOperationException("TODO"); + } + + @JsonIgnore + public AbstractModification toModification() { + throw new UnsupportedOperationException("TODO"); + } + + @JsonIgnore + public final NetworkModificationException.Type getErrorType() { + return NetworkModificationException.Type.valueOf(this.getClass().getAnnotation(ModificationErrorTypeName.class).value()); + } + + public final ModificationType getType() { + return type.get() != null ? type.get() : ModificationType.valueOf(this.getClass().getAnnotation(JsonTypeName.class).value()); + } + + public void setType(ModificationType type) { + this.type.set(type); + } + + @JsonIgnore + public Map getMapMessageValues() { + return Map.of(); + } + + @JsonIgnore + public void check() { + // To check input DTO before hypothesis creation. Nothing to check here + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/OperatingStatusModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/OperatingStatusModificationInfos.java new file mode 100644 index 0000000..995ebeb --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/OperatingStatusModificationInfos.java @@ -0,0 +1,87 @@ +/* + Copyright (c) 2021, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.OperatingStatusModification; +import java.util.HashMap; +import java.util.Map; + +import static org.gridsuite.modification.NetworkModificationException.Type.OPERATING_ACTION_TYPE_EMPTY; + +/** + * @author Slimane Amar + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Operating status modification") +@JsonTypeName("OPERATING_STATUS_MODIFICATION") +@ModificationErrorTypeName("OPERATING_STATUS_MODIFICATION_ERROR") +public class OperatingStatusModificationInfos extends EquipmentModificationInfos { + @Schema(description = "Action type") + private ActionType action; + + @Schema(description = "Energized end one or two voltage level ID") + private String energizedVoltageLevelId; + + public enum ActionType { + LOCKOUT, + TRIP, + SWITCH_ON, + ENERGISE_END_ONE, + ENERGISE_END_TWO + } + + @Override + public AbstractModification toModification() { + return new OperatingStatusModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + String defaultName = switch (action) { + case LOCKOUT -> "Lockout ${equipmentId}"; + case TRIP -> "Trip ${equipmentId}"; + case ENERGISE_END_ONE, ENERGISE_END_TWO -> "Energise ${equipmentId}"; + case SWITCH_ON -> "Switch on ${equipmentId}"; + }; + return reportNode.newReportNode().withMessageTemplate(getType().name() + "_" + action, defaultName).withUntypedValue("equipmentId", this.getEquipmentId()).add(); + } + + @Override + public void check() { + super.check(); + if (action == null) { + throw new NetworkModificationException(OPERATING_ACTION_TYPE_EMPTY); + } + } + + @Override + public Map getMapMessageValues() { + Map mapMessageValues = new HashMap<>(); + mapMessageValues.put("action", getAction().name()); + mapMessageValues.put("equipmentId", getEquipmentId()); + if (getEnergizedVoltageLevelId() != null) { + mapMessageValues.put("energizedVoltageLevelId", getEnergizedVoltageLevelId()); + } + return mapMessageValues; + + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/OperationType.java b/src/main/java/org/gridsuite/modification/dto/OperationType.java new file mode 100644 index 0000000..acbd561 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/OperationType.java @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +/** + * @author Nicolas Noir + */ +public enum OperationType { + SET, + UNSET +} diff --git a/src/main/java/org/gridsuite/modification/dto/PhaseTapChangerCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/PhaseTapChangerCreationInfos.java new file mode 100644 index 0000000..0bccd5a --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/PhaseTapChangerCreationInfos.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.powsybl.iidm.network.PhaseTapChanger; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; + +/** + * @author Hugo Marcellin + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "PhaseTapChanger attributes") +public class PhaseTapChangerCreationInfos extends TapChangerCreationInfos { + + @Schema(description = "regulationMode") + private PhaseTapChanger.RegulationMode regulationMode; + + @Schema(description = "regulationValue") + private Double regulationValue; +} diff --git a/src/main/java/org/gridsuite/modification/dto/PhaseTapChangerModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/PhaseTapChangerModificationInfos.java new file mode 100644 index 0000000..d9208dc --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/PhaseTapChangerModificationInfos.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.powsybl.iidm.network.PhaseTapChanger; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; + +/** + * @author Hugo Marcellin + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@Schema(description = "PhaseTapChanger attributes") +public class PhaseTapChangerModificationInfos extends TapChangerModificationInfos { + + @Schema(description = "regulationMode") + private AttributeModification regulationMode; + + @Schema(description = "regulationValue") + private AttributeModification regulationValue; +} diff --git a/src/main/java/org/gridsuite/modification/dto/RatioTapChangerCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/RatioTapChangerCreationInfos.java new file mode 100644 index 0000000..2fc52f0 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/RatioTapChangerCreationInfos.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author Hugo Marcellin + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "RatioTapChanger attributes") +public class RatioTapChangerCreationInfos extends TapChangerCreationInfos { + + @Schema(description = "hasLoadTapChangingCapabilities") + @JsonProperty("hasLoadTapChangingCapabilities") + private boolean loadTapChangingCapabilities; + + @Schema(description = "targetV") + private Double targetV; +} diff --git a/src/main/java/org/gridsuite/modification/dto/RatioTapChangerModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/RatioTapChangerModificationInfos.java new file mode 100644 index 0000000..2d89d41 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/RatioTapChangerModificationInfos.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; + +/** + * @author Ayoub LABIDI + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "RatioTapChanger attributes") +public class RatioTapChangerModificationInfos extends TapChangerModificationInfos { + + @Schema(description = "hasLoadTapChangingCapabilities") + @JsonProperty("hasLoadTapChangingCapabilities") + private AttributeModification loadTapChangingCapabilities; + + @Schema(description = "targetV") + private AttributeModification targetV; +} diff --git a/src/main/java/org/gridsuite/modification/dto/ReactiveCapabilityCurveCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/ReactiveCapabilityCurveCreationInfos.java new file mode 100644 index 0000000..0737ab0 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ReactiveCapabilityCurveCreationInfos.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +/** + * @author Seddik Yengui + */ + +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Generator reactive capability curve point creation") +public class ReactiveCapabilityCurveCreationInfos { + @Schema(description = "Minimum reactive power ") + private Double minQ; + + @Schema(description = "Maximum reactive power") + private Double maxQ; + + @Schema(description = "Active Power") + private Double p; +} diff --git a/src/main/java/org/gridsuite/modification/dto/ReactiveCapabilityCurveModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/ReactiveCapabilityCurveModificationInfos.java new file mode 100644 index 0000000..c3e36df --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ReactiveCapabilityCurveModificationInfos.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * @author Ayoub LABIDI + */ + +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Data +@Schema(description = "Generator reactive capability curve point creation") +public class ReactiveCapabilityCurveModificationInfos { + @Schema(description = "Minimum reactive power ") + private Double minQ; + + @Schema(description = "Old minimum reactive power ") + private Double oldMinQ; + + @Schema(description = "Maximum reactive power") + private Double maxQ; + + @Schema(description = "Old maximum reactive power") + private Double oldMaxQ; + + @Schema(description = "Active Power") + private Double p; + + @Schema(description = "Active Power") + private Double oldP; +} diff --git a/src/main/java/org/gridsuite/modification/dto/ReactiveLimitsHolderInfos.java b/src/main/java/org/gridsuite/modification/dto/ReactiveLimitsHolderInfos.java new file mode 100644 index 0000000..80e3b9f --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ReactiveLimitsHolderInfos.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto; + +import java.util.List; + +/** + * @author Seddik Yengui + */ + +public interface ReactiveLimitsHolderInfos { + Boolean getReactiveCapabilityCurve(); + + Double getMinQ(); + + Double getMaxQ(); + + List getReactiveCapabilityCurvePoints(); +} diff --git a/src/main/java/org/gridsuite/modification/dto/RegulationSide.java b/src/main/java/org/gridsuite/modification/dto/RegulationSide.java new file mode 100644 index 0000000..99e7760 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/RegulationSide.java @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +/** + * @author Ayoub LABIDI + */ +public enum RegulationSide { + SIDE1, + SIDE2 +} diff --git a/src/main/java/org/gridsuite/modification/dto/ScalingInfos.java b/src/main/java/org/gridsuite/modification/dto/ScalingInfos.java new file mode 100644 index 0000000..85647fc --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ScalingInfos.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.VariationType; + +import java.util.List; + +/** + * @author bendaamerahm + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@Schema(description = "Scaling infos") +@ToString(callSuper = true) +public class ScalingInfos extends ModificationInfos { + @Schema(description = "scaling variations") + private List variations; + + @Schema(description = "variation type") + private VariationType variationType; + +} diff --git a/src/main/java/org/gridsuite/modification/dto/ScalingVariationInfos.java b/src/main/java/org/gridsuite/modification/dto/ScalingVariationInfos.java new file mode 100644 index 0000000..68da53b --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ScalingVariationInfos.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.ReactiveVariationMode; +import org.gridsuite.modification.VariationMode; + +import java.util.List; +import java.util.UUID; + +/** + * @author bendaamerahm + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString +@Schema(description = "Scaling creation") +public class ScalingVariationInfos { + @Schema(description = "id") + private UUID id; + + @Schema(description = "filters") + private List filters; + + @Schema(description = "variation mode") + private VariationMode variationMode; + + @Schema(description = "variation value") + private Double variationValue; + + @Schema(description = "reactiveVariationMode") + private ReactiveVariationMode reactiveVariationMode; + +} diff --git a/src/main/java/org/gridsuite/modification/dto/ShuntCompensatorCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/ShuntCompensatorCreationInfos.java new file mode 100644 index 0000000..b78e682 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ShuntCompensatorCreationInfos.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.ShuntCompensatorCreation; + +/** + * @author Jacques Borsenberger + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Shunt compensator creation") +@JsonTypeName("SHUNT_COMPENSATOR_CREATION") +@ModificationErrorTypeName("CREATE_SHUNT_COMPENSATOR_ERROR") +public class ShuntCompensatorCreationInfos extends InjectionCreationInfos { + @Schema(description = "Maximum number of sections") + private Integer maximumSectionCount; + + @Schema(description = "Section count") + private Integer sectionCount; + + @Schema(description = "Maximal susceptance available") + private Double maxSusceptance; + + @Schema(description = "Qmax available at nominal voltage") + private Double maxQAtNominalV; + + @Schema(description = "Shunt Compensator Type") + private ShuntCompensatorType shuntCompensatorType; + + @Override + public AbstractModification toModification() { + return new ShuntCompensatorCreation(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Shunt compensator creation ${shuntCompensatorId}") + .withUntypedValue("shuntCompensatorId", this.getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/ShuntCompensatorModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/ShuntCompensatorModificationInfos.java new file mode 100644 index 0000000..07355e0 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ShuntCompensatorModificationInfos.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.ShuntCompensatorModification; + +/** + * @author Seddik Yengui + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Shunt compensator modification") +@JsonTypeName("SHUNT_COMPENSATOR_MODIFICATION") +@ModificationErrorTypeName("MODIFY_SHUNT_COMPENSATOR_ERROR") +public class ShuntCompensatorModificationInfos extends InjectionModificationInfos { + + @Schema(description = "Maximum number of sections") + private AttributeModification maximumSectionCount; + + @Schema(description = "Section count") + private AttributeModification sectionCount; + + @Schema(description = "Maximal susceptance available") + private AttributeModification maxSusceptance; + + @Schema(description = "Qmax available at nominal voltage") + private AttributeModification maxQAtNominalV; + + @Schema(description = "Shunt Compensator Type") + private AttributeModification shuntCompensatorType; + + @Override + public AbstractModification toModification() { + return new ShuntCompensatorModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Shunt compensator modification ${shuntCompensatorId}") + .withUntypedValue("shuntCompensatorId", this.getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/ShuntCompensatorType.java b/src/main/java/org/gridsuite/modification/dto/ShuntCompensatorType.java new file mode 100644 index 0000000..4eaf7de --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/ShuntCompensatorType.java @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +/** + * @author Ayoub LABIDI + */ +public enum ShuntCompensatorType { + REACTOR, + CAPACITOR +} diff --git a/src/main/java/org/gridsuite/modification/dto/StaticVarCompensatorCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/StaticVarCompensatorCreationInfos.java new file mode 100644 index 0000000..9a182fd --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/StaticVarCompensatorCreationInfos.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.StaticVarCompensator; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.ModificationType; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.StaticVarCompensatorCreation; + +/** + * @author Ghazwa Rehili + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Static var compensator creation") +@JsonTypeName("STATIC_VAR_COMPENSATOR_CREATION") +@ModificationErrorTypeName("CREATE_STATIC_VAR_COMPENSATOR_ERROR") +public class StaticVarCompensatorCreationInfos extends InjectionCreationInfos { + @Schema(description = "Susceptance max") + private Double maxSusceptance; + + @Schema(description = "Susceptance min") + private Double minSusceptance; + + @Schema(description = "Q max at nominal voltage") + private Double maxQAtNominalV; + + @Schema(description = "Q min at nominal voltage") + private Double minQAtNominalV; + + @Schema(description = "regulation mode") + private StaticVarCompensator.RegulationMode regulationMode; + + @Schema(description = "Voltage set point") + private Double voltageSetpoint; + + @Schema(description = "Reactive power set point") + private Double reactivePowerSetpoint; + + @Schema(description = "Voltage Regulation type") + private VoltageRegulationType voltageRegulationType; + + @Schema(description = "Regulating terminal equipment id") + private String regulatingTerminalId; + + @Schema(description = "Regulating terminal equipment type") + private String regulatingTerminalType; + + @Schema(description = "Regulating terminal voltage level id") + private String regulatingTerminalVlId; + + @Schema(description = "standby automaton on") + private boolean standbyAutomatonOn; + + @Schema(description = "Standby") + private boolean standby; + + @Schema(description = "Fixed part of susceptance") + private Double b0; + + @Schema(description = "Fixed part of Q at nominal voltage") + private Double q0; + + @Schema(description = "Low voltage set point ") + private Double lowVoltageSetpoint; + + @Schema(description = "High voltage set point") + private Double highVoltageSetpoint; + + @Schema(description = "Low voltage threshold") + private Double lowVoltageThreshold; + + @Schema(description = "High voltage threshold") + private Double highVoltageThreshold; + + @Override + public AbstractModification toModification() { + return new StaticVarCompensatorCreation(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode().withMessageTemplate(ModificationType.STATIC_VAR_COMPENSATOR_CREATION.name(), + "Static var compensator creation ${id}").withUntypedValue("id", this.getEquipmentId()).add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/SubstationCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/SubstationCreationInfos.java new file mode 100644 index 0000000..ea637c7 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/SubstationCreationInfos.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2021, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.SubstationCreation; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.Country; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author Abdelsalem Hedhili + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Substation creation") +@JsonTypeName("SUBSTATION_CREATION") +@ModificationErrorTypeName("CREATE_SUBSTATION_ERROR") +public class SubstationCreationInfos extends EquipmentCreationInfos { + + @Schema(description = "Substation country") + private Country country; + + @Override + public AbstractModification toModification() { + return new SubstationCreation(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Substation creation ${substationId}") + .withUntypedValue("substationId", this.getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/SubstationModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/SubstationModificationInfos.java new file mode 100644 index 0000000..da49095 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/SubstationModificationInfos.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.Country; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.SubstationModification; + +/** + * @author David Braquart + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@Schema(description = "Substation modification") +@JsonTypeName("SUBSTATION_MODIFICATION") +@ModificationErrorTypeName("MODIFY_SUBSTATION_ERROR") +public class SubstationModificationInfos extends BasicEquipmentModificationInfos { + @Schema(description = "country modification") + private AttributeModification country; + + @Override + public AbstractModification toModification() { + return new SubstationModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Substation modification ${substationId}") + .withUntypedValue("substationId", this.getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/SubstationsGeneratorsOrderingInfos.java b/src/main/java/org/gridsuite/modification/dto/SubstationsGeneratorsOrderingInfos.java new file mode 100644 index 0000000..168cbb5 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/SubstationsGeneratorsOrderingInfos.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Substation ids for ordering generators with marginal cost") +public class SubstationsGeneratorsOrderingInfos { + @Schema(description = "substation ids") + private List substationIds; +} diff --git a/src/main/java/org/gridsuite/modification/dto/TabularCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/TabularCreationInfos.java new file mode 100644 index 0000000..7ff7ce8 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/TabularCreationInfos.java @@ -0,0 +1,65 @@ +/* + Copyright (c) 2024, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.ModificationType; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.TabularCreation; +import org.springframework.lang.NonNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Franck Lecuyer + */ +@EqualsAndHashCode(callSuper = true) +@SuperBuilder +@NoArgsConstructor +@Data +@Schema(description = "Tabular creation") +@JsonTypeName("TABULAR_CREATION") +@ModificationErrorTypeName("TABULAR_CREATION_ERROR") +public class TabularCreationInfos extends ModificationInfos { + + @Schema(description = "Creation type") + @NonNull + private ModificationType creationType; + + @Schema(description = "Creations") + @JsonInclude(JsonInclude.Include.NON_NULL) + private List creations; + + @Override + public AbstractModification toModification() { + return new TabularCreation(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(ModificationType.TABULAR_CREATION.name(), "Tabular creation") + .add(); + } + + @Override + public Map getMapMessageValues() { + Map mapMessageValues = new HashMap<>(); + mapMessageValues.put("tabularCreationType", getCreationType().name()); + return mapMessageValues; + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/TabularModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/TabularModificationInfos.java new file mode 100644 index 0000000..9e14130 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/TabularModificationInfos.java @@ -0,0 +1,63 @@ +/* + Copyright (c) 2023, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import lombok.*; +import org.gridsuite.modification.ModificationType; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.TabularModification; +import org.springframework.lang.NonNull; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.experimental.SuperBuilder; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Etienne Homer + */ +@SuperBuilder +@NoArgsConstructor +@Data +@Schema(description = "Tabular modification") +@JsonTypeName("TABULAR_MODIFICATION") +@ModificationErrorTypeName("TABULAR_MODIFICATION_ERROR") +public class TabularModificationInfos extends ModificationInfos { + + @Schema(description = "Modification type") + @NonNull + private ModificationType modificationType; + + @Schema(description = "modifications") + @JsonInclude(JsonInclude.Include.NON_NULL) + private List modifications; + + @Override + public AbstractModification toModification() { + return new TabularModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(ModificationType.TABULAR_CREATION.name(), "Tabular modification") + .add(); + } + + @Override + public Map getMapMessageValues() { + Map mapMessageValues = new HashMap<>(); + mapMessageValues.put("tabularModificationType", getModificationType().name()); + return mapMessageValues; + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/TapChangerCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/TapChangerCreationInfos.java new file mode 100644 index 0000000..140263f --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/TapChangerCreationInfos.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author Hugo Marcellin + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString +@Schema(description = "TapChanger attributes") +public class TapChangerCreationInfos { + + @Schema(description = "lowTapPosition") + private int lowTapPosition; + + @Schema(description = "tapPosition") + private int tapPosition; + + @Schema(description = "isRegulating") + @JsonProperty("isRegulating") + private boolean regulating; + + @Schema(description = "targetDeadband") + private Double targetDeadband; + + @Schema(description = "Regulating terminal equipment id") + private String regulatingTerminalId; + + @Schema(description = "Regulating terminal equipment type") + private String regulatingTerminalType; + + @Schema(description = "Regulating terminal voltage level id") + private String regulatingTerminalVlId; + + @Schema(description = "steps") + private List steps; +} diff --git a/src/main/java/org/gridsuite/modification/dto/TapChangerModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/TapChangerModificationInfos.java new file mode 100644 index 0000000..e71c54f --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/TapChangerModificationInfos.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author Ayoub LABIDI enabled; + + @Schema(description = "Regulation type") + private AttributeModification regulationType; + + @Schema(description = "Side") + private AttributeModification regulationSide; + + @Schema(description = "lowTapPosition") + private AttributeModification lowTapPosition; + + @Schema(description = "tapPosition") + private AttributeModification tapPosition; + + @Schema(description = "isRegulating") + @JsonProperty("isRegulating") + private AttributeModification regulating; + + @Schema(description = "targetDeadband") + private AttributeModification targetDeadband; + + @Schema(description = "Regulating terminal equipment id") + private AttributeModification regulatingTerminalId; + + @Schema(description = "Regulating terminal equipment type") + private AttributeModification regulatingTerminalType; + + @Schema(description = "Regulating terminal voltage level id") + private AttributeModification regulatingTerminalVlId; + + @Schema(description = "steps") + private List steps; +} diff --git a/src/main/java/org/gridsuite/modification/dto/TapChangerStepCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/TapChangerStepCreationInfos.java new file mode 100644 index 0000000..95b08f9 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/TapChangerStepCreationInfos.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +/** + * @author Hugo Marcellin + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class TapChangerStepCreationInfos { + + @Schema(description = "index") + private int index; + + @Schema(description = "rho") + private double rho; + + @Schema(description = "r") + private double r; + + @Schema(description = "x") + private double x; + + @Schema(description = "g") + private double g; + + @Schema(description = "b") + private double b; + + @Schema(description = "alpha") + private double alpha; +} diff --git a/src/main/java/org/gridsuite/modification/dto/TemporaryLimitModificationType.java b/src/main/java/org/gridsuite/modification/dto/TemporaryLimitModificationType.java new file mode 100644 index 0000000..d1e87ea --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/TemporaryLimitModificationType.java @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +/** + * @author Ayoub LABIDI + */ +public enum TemporaryLimitModificationType { + ADDED, + MODIFIED, + DELETED, +} diff --git a/src/main/java/org/gridsuite/modification/dto/TwoWindingsTransformerCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/TwoWindingsTransformerCreationInfos.java new file mode 100644 index 0000000..bf6e070 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/TwoWindingsTransformerCreationInfos.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2021, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.TwoWindingsTransformerCreation; + +/** + * @author Abdelsalem Hedhili + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Two windings transformer creation") +@JsonTypeName("TWO_WINDINGS_TRANSFORMER_CREATION") +@ModificationErrorTypeName("CREATE_TWO_WINDINGS_TRANSFORMER_ERROR") +public class TwoWindingsTransformerCreationInfos extends BranchCreationInfos { + + @Schema(description = "Magnetizing conductance") + private double g; + + @Schema(description = "Magnetizing susceptance") + private double b; + + @Schema(description = "side 1 rated voltage") + private double ratedU1; + + @Schema(description = "side 2 rated voltage") + private double ratedU2; + + @Schema(description = "Rated conductance in Siemens") + private Double ratedS; + + @Schema(description = "Ratio tap changer") + private RatioTapChangerCreationInfos ratioTapChanger; + + @Schema(description = "Phase tap changer") + private PhaseTapChangerCreationInfos phaseTapChanger; + + @Override + public AbstractModification toModification() { + return new TwoWindingsTransformerCreation(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Two windings transformer creation ${twoWindingsTransformerId}") + .withUntypedValue("twoWindingsTransformerId", getEquipmentId()) + .add(); + } + +} diff --git a/src/main/java/org/gridsuite/modification/dto/TwoWindingsTransformerModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/TwoWindingsTransformerModificationInfos.java new file mode 100644 index 0000000..ab1e2b2 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/TwoWindingsTransformerModificationInfos.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.TwoWindingsTransformerModification; + +/** + * @author Florent MILLOT + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Two windings transformer modification") +@JsonTypeName("TWO_WINDINGS_TRANSFORMER_MODIFICATION") +@ModificationErrorTypeName("MODIFY_TWO_WINDINGS_TRANSFORMER_ERROR") +public class TwoWindingsTransformerModificationInfos extends BranchModificationInfos { + + @Schema(description = "Magnetizing conductance") + private AttributeModification g; + + @Schema(description = "Magnetizing susceptance") + private AttributeModification b; + + @Schema(description = "Side 1 rated voltage") + private AttributeModification ratedU1; + + @Schema(description = "Side 2 rated voltage") + private AttributeModification ratedU2; + + @Schema(description = "Rated conductance in Siemens") + private AttributeModification ratedS; + + @Schema(description = "Ratio tap changer") + @Builder.Default + private RatioTapChangerModificationInfos ratioTapChanger = new RatioTapChangerModificationInfos(); + + @Schema(description = "Phase tap changer") + @Builder.Default + private PhaseTapChangerModificationInfos phaseTapChanger = new PhaseTapChangerModificationInfos(); + + @Override + public AbstractModification toModification() { + return new TwoWindingsTransformerModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Two windings transformer modification ${twoWindingsTransformerId}") + .withUntypedValue("twoWindingsTransformerId", getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/VoltageInitBusModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/VoltageInitBusModificationInfos.java new file mode 100644 index 0000000..b5649c8 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VoltageInitBusModificationInfos.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +/** + * @author Ayoub LABIDI + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Voltage init bus modfication infos") +public class VoltageInitBusModificationInfos { + @Schema(description = "Voltage level id") + private String voltageLevelId; + + @Schema(description = "Bus id") + private String busId; + + @Schema(description = "Voltage magnitude") + private Double v; + + @Schema(description = "Voltage angle") + private Double angle; + +} diff --git a/src/main/java/org/gridsuite/modification/dto/VoltageInitGeneratorModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/VoltageInitGeneratorModificationInfos.java new file mode 100644 index 0000000..e9ac4b9 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VoltageInitGeneratorModificationInfos.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Voltage init generator modfication infos") +public class VoltageInitGeneratorModificationInfos { + @Schema(description = "Generator id") + private String generatorId; + + @Schema(description = "Voltage set point") + private Double targetV; + + @Schema(description = "Reactive power set point") + private Double targetQ; +} diff --git a/src/main/java/org/gridsuite/modification/dto/VoltageInitModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/VoltageInitModificationInfos.java new file mode 100644 index 0000000..81f4c8e --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VoltageInitModificationInfos.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.ModificationType; +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.VoltageInitModification; + +import java.util.List; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@Schema(description = "Voltage init modification infos") +@JsonTypeName("VOLTAGE_INIT_MODIFICATION") +@ModificationErrorTypeName("VOLTAGE_INIT_MODIFICATION_ERROR") +public class VoltageInitModificationInfos extends ModificationInfos { + @Schema(description = "generators modifications") + private List generators; + + @Schema(description = "transformers modifications") + private List transformers; + + @Schema(description = "static var compensator modifications") + private List staticVarCompensators; + + @Schema(description = "vsc converter station modifications") + private List vscConverterStations; + + @Schema(description = "shunt compensator modifications") + private List shuntCompensators; + + @Schema(description = "buses modifications") + private List buses; + + @Override + public AbstractModification toModification() { + return new VoltageInitModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode().withMessageTemplate(ModificationType.VOLTAGE_INIT_MODIFICATION.name(), "Voltage init modification").add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/VoltageInitShuntCompensatorModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/VoltageInitShuntCompensatorModificationInfos.java new file mode 100644 index 0000000..20aa40e --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VoltageInitShuntCompensatorModificationInfos.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Voltage init shunt compensator modification infos") +public class VoltageInitShuntCompensatorModificationInfos { + @Schema(description = "Shunt compensator id") + private String shuntCompensatorId; + + @Schema(description = "Section count") + private Integer sectionCount; + + @Schema(description = "Connexion") + private Boolean connect; + + @Schema(description = "Target voltage") + private Double targetV; +} diff --git a/src/main/java/org/gridsuite/modification/dto/VoltageInitStaticVarCompensatorModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/VoltageInitStaticVarCompensatorModificationInfos.java new file mode 100644 index 0000000..8fe2761 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VoltageInitStaticVarCompensatorModificationInfos.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Voltage init static var compensator modfication infos") +public class VoltageInitStaticVarCompensatorModificationInfos { + @Schema(description = "Static var compensator id") + private String staticVarCompensatorId; + + @Schema(description = "Voltage set point") + private Double voltageSetpoint; + + @Schema(description = "Reactive power set point") + private Double reactivePowerSetpoint; +} diff --git a/src/main/java/org/gridsuite/modification/dto/VoltageInitTransformerModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/VoltageInitTransformerModificationInfos.java new file mode 100644 index 0000000..f87b45d --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VoltageInitTransformerModificationInfos.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.powsybl.iidm.network.ThreeSides; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Voltage init transformer modification infos") +public class VoltageInitTransformerModificationInfos { + @Schema(description = "Transformer id") + private String transformerId; + + @Schema(description = "Ratio tap changer position") + private Integer ratioTapChangerPosition; + + @Schema(description = "Ratio tap changer target voltage") + private Double ratioTapChangerTargetV; + + @Schema(description = "3 windings transformer leg side") + private ThreeSides legSide; +} diff --git a/src/main/java/org/gridsuite/modification/dto/VoltageInitVscConverterStationModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/VoltageInitVscConverterStationModificationInfos.java new file mode 100644 index 0000000..93aa7d8 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VoltageInitVscConverterStationModificationInfos.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +/** + * @author Franck Lecuyer + */ +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Schema(description = "Voltage init vsc converter station modfication infos") +public class VoltageInitVscConverterStationModificationInfos { + @Schema(description = "Vsc converter station id") + private String vscConverterStationId; + + @Schema(description = "Voltage set point") + private Double voltageSetpoint; + + @Schema(description = "Reactive power set point") + private Double reactivePowerSetpoint; +} diff --git a/src/main/java/org/gridsuite/modification/dto/VoltageLevelCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/VoltageLevelCreationInfos.java new file mode 100644 index 0000000..dd1d301 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VoltageLevelCreationInfos.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.SwitchKind; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.VoltageLevelCreation; + +import java.util.List; + +/** + * @author Laurent GARNIER + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Voltage level creation") +@JsonTypeName("VOLTAGE_LEVEL_CREATION") +@ModificationErrorTypeName("CREATE_VOLTAGE_LEVEL_ERROR") +public class VoltageLevelCreationInfos extends EquipmentCreationInfos { + + @Schema(description = "substation id") + private String substationId; + + @Schema(description = "nominal voltage in kV") + private double nominalV; + + @Schema(description = "low voltage limit in kV") + private Double lowVoltageLimit; + + @Schema(description = "high voltage limit in kV") + private Double highVoltageLimit; + + @Schema(description = "low short-circuit current limit in A") + private Double ipMin; + + @Schema(description = "high short-circuit current limit in A") + private Double ipMax; + + @Schema(description = "busbar Count") + private int busbarCount; + + @Schema(description = "section Count") + private int sectionCount; + + @Schema(description = "switchKinds") + private List switchKinds; + + @Schema(description = "coupling devices infos") + private List couplingDevices; + + @Override + public AbstractModification toModification() { + return new VoltageLevelCreation(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "VoltageLevel creation ${voltageLevelId}") + .withUntypedValue("voltageLevelId", getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/VoltageLevelModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/VoltageLevelModificationInfos.java new file mode 100644 index 0000000..b6a026b --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VoltageLevelModificationInfos.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.VoltageLevelModification; + +/** + * @author Seddik Yengui + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@Schema(description = "Voltage level modification") +@JsonTypeName("VOLTAGE_LEVEL_MODIFICATION") +@ModificationErrorTypeName("MODIFY_VOLTAGE_LEVEL_ERROR") +public class VoltageLevelModificationInfos extends BasicEquipmentModificationInfos { + @Schema(description = "nominal voltage in kV") + private AttributeModification nominalV; + + @Schema(description = "low voltage limit in kV") + private AttributeModification lowVoltageLimit; + + @Schema(description = "high voltage limit in kV") + private AttributeModification highVoltageLimit; + + @Schema(description = "low short-circuit current limit in A") + private AttributeModification ipMin; + + @Schema(description = "high short-circuit current limit in A") + private AttributeModification ipMax; + + @Override + public AbstractModification toModification() { + return new VoltageLevelModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "VoltageLevel modification ${voltageLevelId}") + .withUntypedValue("voltageLevelId", getEquipmentId()) + .add(); + } + +} diff --git a/src/main/java/org/gridsuite/modification/dto/VoltageRegulationType.java b/src/main/java/org/gridsuite/modification/dto/VoltageRegulationType.java new file mode 100644 index 0000000..356242b --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VoltageRegulationType.java @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto; + +/** + * @author Ayoub LABIDI + */ +public enum VoltageRegulationType { + LOCAL, + DISTANT +} diff --git a/src/main/java/org/gridsuite/modification/dto/VscCreationInfos.java b/src/main/java/org/gridsuite/modification/dto/VscCreationInfos.java new file mode 100644 index 0000000..ffa8272 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VscCreationInfos.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.HvdcLine; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.VscCreation; + +/** + * @author Seddik Yengui + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "VSC creation") +@JsonTypeName("VSC_CREATION") +@ModificationErrorTypeName("CREATE_VSC_ERROR") +public class VscCreationInfos extends EquipmentCreationInfos { + @Schema(description = "DC nominal voltage") + private Double nominalV; + + @Schema(description = "DC resistance") + private Double r; + + @Schema(description = "Maximum active power ") + private Double maxP; + + @Schema(description = "Operator active power limit (Side1->Side2)") + private Float operatorActivePowerLimitFromSide1ToSide2; + + @Schema(description = "Operator active power limit (Side2->Side1)") + private Float operatorActivePowerLimitFromSide2ToSide1; + + @Schema(description = "Converters mode") + private HvdcLine.ConvertersMode convertersMode; + + @Schema(description = "Active power setpoint") + private Double activePowerSetpoint; + + @Schema(description = "Angle droop active power control ") + private Boolean angleDroopActivePowerControl; + + @Schema(description = "p0") + private Float p0; + + @Schema(description = "droop") + private Float droop; + + @Schema(description = "Converter station 1") + private ConverterStationCreationInfos converterStation1; + + @Schema(description = "Converter station 2") + private ConverterStationCreationInfos converterStation2; + + @Override + public AbstractModification toModification() { + return new VscCreation(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Vsc creation ${vscId}") + .withUntypedValue("vscId", getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/VscModificationInfos.java b/src/main/java/org/gridsuite/modification/dto/VscModificationInfos.java new file mode 100644 index 0000000..b19d156 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/VscModificationInfos.java @@ -0,0 +1,74 @@ +package org.gridsuite.modification.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.HvdcLine; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.modifications.VscModification; + +/** + * @author jamal kheyyad + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "VSC modification") +@JsonTypeName("VSC_MODIFICATION") +@ModificationErrorTypeName("MODIFY_VSC_ERROR") +public class VscModificationInfos extends BasicEquipmentModificationInfos { + @Schema(description = "DC nominal voltage") + private AttributeModification nominalV; + + @Schema(description = "DC resistance") + private AttributeModification r; + + @Schema(description = "Maximum active power ") + private AttributeModification maxP; + + @Schema(description = "Operator active power limit (Side1->Side2)") + private AttributeModification operatorActivePowerLimitFromSide1ToSide2; + + @Schema(description = "Operator active power limit (Side2->Side1)") + private AttributeModification operatorActivePowerLimitFromSide2ToSide1; + + @Schema(description = "Converters mode") + private AttributeModification convertersMode; + + @Schema(description = "Active power setpoint") + private AttributeModification activePowerSetpoint; + + @Schema(description = "Angle droop active power control ") + private AttributeModification angleDroopActivePowerControl; + + @Schema(description = "p0") + private AttributeModification p0; + + @Schema(description = "droop") + private AttributeModification droop; + + @Schema(description = "Converter station 1") + private ConverterStationModificationInfos converterStation1; + + @Schema(description = "Converter station 2") + private ConverterStationModificationInfos converterStation2; + + @Override + public AbstractModification toModification() { + return new VscModification(this); + } + + @Override + public ReportNode createSubReportNode(ReportNode reportNode) { + return reportNode.newReportNode() + .withMessageTemplate(getType().name(), "Vsc modification ${vscId}") + .withUntypedValue("vscId", getEquipmentId()) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/annotation/ModificationErrorTypeName.java b/src/main/java/org/gridsuite/modification/dto/annotation/ModificationErrorTypeName.java new file mode 100644 index 0000000..4f366cc --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/annotation/ModificationErrorTypeName.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.dto.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Slimane Amar + */ +@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ModificationErrorTypeName { + public String value() default ""; +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/AbstractAssignmentInfos.java b/src/main/java/org/gridsuite/modification/dto/byfilter/AbstractAssignmentInfos.java new file mode 100644 index 0000000..4fccff4 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/AbstractAssignmentInfos.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.FilterInfos; + +import java.util.List; +import java.util.UUID; + +/** + * @author Thang PHAM + */ +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +public abstract class AbstractAssignmentInfos { + @Schema(description = "id") + private UUID id; + + @Schema(description = "List of filters") + private List filters; + + @Schema(description = "Edited field") + private String editedField; + + @JsonIgnore + public String getEditedFieldLabel() { + return editedField; + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/DataType.java b/src/main/java/org/gridsuite/modification/dto/byfilter/DataType.java new file mode 100644 index 0000000..bad3b57 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/DataType.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter; + +/** + * @author Thang PHAM + */ +public enum DataType { + ENUM, + BOOLEAN, + INTEGER, + DOUBLE, + PROPERTY +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/AssignmentInfos.java b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/AssignmentInfos.java new file mode 100644 index 0000000..b969791 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/AssignmentInfos.java @@ -0,0 +1,48 @@ + /** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.assignment; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.byfilter.AbstractAssignmentInfos; +import org.gridsuite.modification.dto.byfilter.DataType; + +/** + * @author Thang PHAM + */ +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "dataType", + include = JsonTypeInfo.As.EXISTING_PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(value = BooleanAssignmentInfos.class, name = "BOOLEAN"), + @JsonSubTypes.Type(value = EnumAssignmentInfos.class, name = "ENUM"), + @JsonSubTypes.Type(value = DoubleAssignmentInfos.class, name = "DOUBLE"), + @JsonSubTypes.Type(value = IntegerAssignmentInfos.class, name = "INTEGER"), + @JsonSubTypes.Type(value = PropertyAssignmentInfos.class, name = "PROPERTY"), +}) +@JsonInclude(JsonInclude.Include.NON_NULL) +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +public class AssignmentInfos extends AbstractAssignmentInfos { + @Schema(description = "Value") + private T value; + + public DataType getDataType() { + throw new UnsupportedOperationException("This method should not be called"); + } + +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/BooleanAssignmentInfos.java b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/BooleanAssignmentInfos.java new file mode 100644 index 0000000..3ea15ac --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/BooleanAssignmentInfos.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.assignment; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.byfilter.DataType; + +/** + * @author Thang PHAM + */ +@SuperBuilder +@NoArgsConstructor +public class BooleanAssignmentInfos extends AssignmentInfos { + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public DataType getDataType() { + return DataType.BOOLEAN; + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/DoubleAssignmentInfos.java b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/DoubleAssignmentInfos.java new file mode 100644 index 0000000..229d119 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/DoubleAssignmentInfos.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.assignment; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.byfilter.DataType; + +/** + * @author Thang PHAM + */ +@SuperBuilder +@NoArgsConstructor +public class DoubleAssignmentInfos extends AssignmentInfos { + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public DataType getDataType() { + return DataType.DOUBLE; + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/EnumAssignmentInfos.java b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/EnumAssignmentInfos.java new file mode 100644 index 0000000..dba96d5 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/EnumAssignmentInfos.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.assignment; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.byfilter.DataType; + +/** + * @author Thang PHAM + */ +@SuperBuilder +@NoArgsConstructor +public class EnumAssignmentInfos extends AssignmentInfos { + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public DataType getDataType() { + return DataType.ENUM; + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/IntegerAssignmentInfos.java b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/IntegerAssignmentInfos.java new file mode 100644 index 0000000..b34a352 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/IntegerAssignmentInfos.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.assignment; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.byfilter.DataType; + +/** + * @author Thang PHAM + */ +@SuperBuilder +@NoArgsConstructor +public class IntegerAssignmentInfos extends AssignmentInfos { + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public DataType getDataType() { + return DataType.INTEGER; + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/PropertyAssignmentInfos.java b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/PropertyAssignmentInfos.java new file mode 100644 index 0000000..b0981b0 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/assignment/PropertyAssignmentInfos.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.assignment; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.byfilter.DataType; + +/** + * @author Thang PHAM + */ +@SuperBuilder +@NoArgsConstructor +public class PropertyAssignmentInfos extends AssignmentInfos { + @Schema(description = "Property name") + @Getter + private String propertyName; + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public DataType getDataType() { + return DataType.PROPERTY; + } + + @JsonIgnore + @Override + public String getEditedFieldLabel() { + return propertyName + " " + super.getEditedFieldLabel(); + } + +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/BatteryField.java b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/BatteryField.java new file mode 100644 index 0000000..649b184 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/BatteryField.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.equipmentfield; + +import com.powsybl.iidm.network.Battery; +import com.powsybl.iidm.network.extensions.ActivePowerControl; +import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; +import jakarta.validation.constraints.NotNull; +import org.gridsuite.modification.dto.AttributeModification; +import org.gridsuite.modification.dto.OperationType; +import org.gridsuite.modification.utils.ModificationUtils; + +import static org.gridsuite.modification.NetworkModificationException.Type.MODIFY_BATTERY_ERROR; +import static org.gridsuite.modification.modifications.BatteryModification.*; + +/** + * @author Seddik Yengui + */ + +public enum BatteryField { + MINIMUM_ACTIVE_POWER, + MAXIMUM_ACTIVE_POWER, + ACTIVE_POWER_SET_POINT, + REACTIVE_POWER_SET_POINT, + DROOP; + + public static String getReferenceValue(Battery battery, String batteryField) { + ActivePowerControl activePowerControl = battery.getExtension(ActivePowerControl.class); + BatteryField field = BatteryField.valueOf(batteryField); + return switch (field) { + case MINIMUM_ACTIVE_POWER -> String.valueOf(battery.getMinP()); + case MAXIMUM_ACTIVE_POWER -> String.valueOf(battery.getMaxP()); + case ACTIVE_POWER_SET_POINT -> String.valueOf(battery.getTargetP()); + case REACTIVE_POWER_SET_POINT -> String.valueOf(battery.getTargetQ()); + case DROOP -> activePowerControl != null ? String.valueOf(activePowerControl.getDroop()) : null; + }; + } + + public static void setNewValue(Battery battery, String batteryField, @NotNull String newValue) { + BatteryField field = BatteryField.valueOf(batteryField); + String errorMessage = String.format(ERROR_MESSAGE, battery.getId()); + final AttributeModification attributeModification = new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET); + switch (field) { + case MINIMUM_ACTIVE_POWER -> + modifyBatteryActiveLimitsAttributes(null, attributeModification, battery, null); + case MAXIMUM_ACTIVE_POWER -> + modifyBatteryActiveLimitsAttributes(attributeModification, null, battery, null); + case ACTIVE_POWER_SET_POINT -> { + ModificationUtils.getInstance().checkActivePowerZeroOrBetweenMinAndMaxActivePower( + attributeModification, null, null, battery.getMinP(), + battery.getMaxP(), battery.getTargetP(), MODIFY_BATTERY_ERROR, errorMessage + ); + modifyBatterySetpointsAttributes(attributeModification, null, null, null, battery, null); + } + case REACTIVE_POWER_SET_POINT -> modifyBatterySetpointsAttributes( + null, attributeModification, null, null, battery, null); + case DROOP -> { + ActivePowerControl activePowerControl = battery.getExtension(ActivePowerControl.class); + ActivePowerControlAdder activePowerControlAdder = battery.newExtension(ActivePowerControlAdder.class); + ModificationUtils.getInstance().modifyActivePowerControlAttributes( + activePowerControl, activePowerControlAdder, null, + new AttributeModification<>(Float.parseFloat(newValue), OperationType.SET), null, + null, MODIFY_BATTERY_ERROR, errorMessage); + } + } + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/FieldUtils.java b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/FieldUtils.java new file mode 100644 index 0000000..6693230 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/FieldUtils.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.equipmentfield; + +import com.powsybl.iidm.network.*; +import org.gridsuite.modification.NetworkModificationException; + +import javax.annotation.Nullable; + +/** + * @author Thang PHAM + */ +public final class FieldUtils { + + private FieldUtils() { + + } + + @Nullable + public static String getFieldValue(Identifiable equipment, String equipmentField) { + return switch (equipment.getType()) { + case GENERATOR -> GeneratorField.getReferenceValue((Generator) equipment, equipmentField); + case BATTERY -> BatteryField.getReferenceValue((Battery) equipment, equipmentField); + case SHUNT_COMPENSATOR -> + ShuntCompensatorField.getReferenceValue((ShuntCompensator) equipment, equipmentField); + case VOLTAGE_LEVEL -> VoltageLevelField.getReferenceValue((VoltageLevel) equipment, equipmentField); + case LOAD -> LoadField.getReferenceValue((Load) equipment, equipmentField); + case TWO_WINDINGS_TRANSFORMER -> + TwoWindingsTransformerField.getReferenceValue((TwoWindingsTransformer) equipment, equipmentField); + default -> throw new NetworkModificationException(NetworkModificationException.Type.MODIFICATION_ERROR, + "Unsupported getting value for equipment type : " + equipment.getType().name()); + }; + } + + public static void setFieldValue(Identifiable equipment, String equipmentField, String newValue) { + switch (equipment.getType()) { + case GENERATOR -> GeneratorField.setNewValue((Generator) equipment, equipmentField, newValue); + case BATTERY -> BatteryField.setNewValue((Battery) equipment, equipmentField, newValue); + case SHUNT_COMPENSATOR -> ShuntCompensatorField.setNewValue((ShuntCompensator) equipment, equipmentField, newValue); + case VOLTAGE_LEVEL -> VoltageLevelField.setNewValue((VoltageLevel) equipment, equipmentField, newValue); + case LOAD -> LoadField.setNewValue((Load) equipment, equipmentField, newValue); + case TWO_WINDINGS_TRANSFORMER -> TwoWindingsTransformerField.setNewValue((TwoWindingsTransformer) equipment, equipmentField, newValue); + default -> throw new NetworkModificationException(NetworkModificationException.Type.MODIFICATION_ERROR, + "Unsupported setting value for equipment type : " + equipment.getType().name()); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/GeneratorField.java b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/GeneratorField.java new file mode 100644 index 0000000..9273be4 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/GeneratorField.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.equipmentfield; + +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.extensions.*; +import com.powsybl.network.store.iidm.impl.extensions.CoordinatedReactiveControlAdderImpl; +import jakarta.validation.constraints.NotNull; +import org.gridsuite.modification.dto.AttributeModification; +import org.gridsuite.modification.dto.OperationType; +import org.gridsuite.modification.utils.ModificationUtils; + +import static org.gridsuite.modification.NetworkModificationException.Type.MODIFY_GENERATOR_ERROR; +import static org.gridsuite.modification.modifications.GeneratorModification.*; + +/** + * @author Seddik Yengui + */ +public enum GeneratorField { + VOLTAGE_REGULATOR_ON, + MINIMUM_ACTIVE_POWER, + MAXIMUM_ACTIVE_POWER, + RATED_NOMINAL_POWER, + ACTIVE_POWER_SET_POINT, + REACTIVE_POWER_SET_POINT, + VOLTAGE_SET_POINT, + PLANNED_ACTIVE_POWER_SET_POINT, + MARGINAL_COST, + PLANNED_OUTAGE_RATE, + FORCED_OUTAGE_RATE, + DROOP, + TRANSIENT_REACTANCE, + STEP_UP_TRANSFORMER_REACTANCE, + Q_PERCENT; + + public static String getReferenceValue(Generator generator, String generatorField) { + ActivePowerControl activePowerControl = generator.getExtension(ActivePowerControl.class); + GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); + GeneratorShortCircuit generatorShortCircuit = generator.getExtension(GeneratorShortCircuit.class); + CoordinatedReactiveControl coordinatedReactiveControl = generator.getExtension(CoordinatedReactiveControl.class); + GeneratorField field = GeneratorField.valueOf(generatorField); + return switch (field) { + case VOLTAGE_REGULATOR_ON -> String.valueOf(generator.isVoltageRegulatorOn()); + case MAXIMUM_ACTIVE_POWER -> String.valueOf(generator.getMaxP()); + case MINIMUM_ACTIVE_POWER -> String.valueOf(generator.getMinP()); + case ACTIVE_POWER_SET_POINT -> String.valueOf(generator.getTargetP()); + case RATED_NOMINAL_POWER -> String.valueOf(generator.getRatedS()); + case REACTIVE_POWER_SET_POINT -> String.valueOf(generator.getTargetQ()); + case VOLTAGE_SET_POINT -> String.valueOf(generator.getTargetV()); + case PLANNED_ACTIVE_POWER_SET_POINT -> generatorStartup != null ? String.valueOf(generatorStartup.getPlannedActivePowerSetpoint()) : null; + case MARGINAL_COST -> generatorStartup != null ? String.valueOf(generatorStartup.getMarginalCost()) : null; + case PLANNED_OUTAGE_RATE -> generatorStartup != null ? String.valueOf(generatorStartup.getPlannedOutageRate()) : null; + case FORCED_OUTAGE_RATE -> generatorStartup != null ? String.valueOf(generatorStartup.getForcedOutageRate()) : null; + case DROOP -> activePowerControl != null ? String.valueOf(activePowerControl.getDroop()) : null; + case TRANSIENT_REACTANCE -> generatorShortCircuit != null ? String.valueOf(generatorShortCircuit.getDirectTransX()) : null; + case STEP_UP_TRANSFORMER_REACTANCE -> generatorShortCircuit != null ? String.valueOf(generatorShortCircuit.getStepUpTransformerX()) : null; + case Q_PERCENT -> coordinatedReactiveControl != null ? String.valueOf(coordinatedReactiveControl.getQPercent()) : null; + }; + } + + public static void setNewValue(Generator generator, String generatorField, @NotNull String newValue) { + GeneratorField field = GeneratorField.valueOf(generatorField); + String errorMessage = String.format(ERROR_MESSAGE, generator.getId()); + switch (field) { + case MAXIMUM_ACTIVE_POWER -> modifyGeneratorActiveLimitsAttributes( + new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), + null, null, generator, null); + case MINIMUM_ACTIVE_POWER -> modifyGeneratorActiveLimitsAttributes( + null, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), null, generator, null); + case ACTIVE_POWER_SET_POINT -> { + ModificationUtils.getInstance().checkActivePowerZeroOrBetweenMinAndMaxActivePower( + new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), + null, null, generator.getMinP(), generator.getMaxP(), generator.getTargetP(), + MODIFY_GENERATOR_ERROR, errorMessage + ); + generator.setTargetP(Double.parseDouble(newValue)); + } + case RATED_NOMINAL_POWER -> modifyGeneratorActiveLimitsAttributes( + null, null, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), generator, null); + case REACTIVE_POWER_SET_POINT -> modifyTargetQ(generator, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET)); + case VOLTAGE_SET_POINT -> modifyTargetV(generator, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET)); + case PLANNED_ACTIVE_POWER_SET_POINT -> modifyGeneratorStartUpAttributes( + new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), null, + null, null, generator, null, null); + case MARGINAL_COST -> modifyGeneratorStartUpAttributes( + null, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), + null, null, generator, null, null); + case PLANNED_OUTAGE_RATE -> modifyGeneratorStartUpAttributes( + null, null, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), + null, generator, null, null); + case FORCED_OUTAGE_RATE -> + modifyGeneratorStartUpAttributes(null, null, null, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), generator, null, null); + case DROOP -> { + ActivePowerControl activePowerControl = generator.getExtension(ActivePowerControl.class); + ActivePowerControlAdder activePowerControlAdder = generator.newExtension(ActivePowerControlAdder.class); + ModificationUtils.getInstance().modifyActivePowerControlAttributes(activePowerControl, activePowerControlAdder, null, + new AttributeModification<>(Float.parseFloat(newValue), OperationType.SET), null, null, + MODIFY_GENERATOR_ERROR, errorMessage); + } + case TRANSIENT_REACTANCE -> modifyGeneratorShortCircuitAttributes( + new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), + null, generator, null); + case STEP_UP_TRANSFORMER_REACTANCE -> modifyGeneratorShortCircuitAttributes( + null, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), generator, null); + case Q_PERCENT -> generator.newExtension(CoordinatedReactiveControlAdderImpl.class) + .withQPercent(Double.parseDouble(newValue)) + .add(); + case VOLTAGE_REGULATOR_ON -> generator.setVoltageRegulatorOn(Boolean.parseBoolean(newValue)); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/LoadField.java b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/LoadField.java new file mode 100644 index 0000000..e5fc0af --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/LoadField.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.equipmentfield; + +import com.powsybl.iidm.network.Load; +import org.gridsuite.modification.dto.AttributeModification; +import org.gridsuite.modification.dto.OperationType; + +import static org.gridsuite.modification.modifications.LoadModification.modifyP0; +import static org.gridsuite.modification.modifications.LoadModification.modifyQ0; +import com.powsybl.iidm.network.LoadType; +import jakarta.validation.constraints.NotNull; + +/** + * @author Seddik Yengui + */ +public enum LoadField { + LOAD_TYPE, + ACTIVE_POWER, + REACTIVE_POWER; + + public static String getReferenceValue(Load load, String loadField) { + LoadField field = LoadField.valueOf(loadField); + return switch (field) { + case LOAD_TYPE -> load.getLoadType().name(); + case ACTIVE_POWER -> String.valueOf(load.getP0()); + case REACTIVE_POWER -> String.valueOf(load.getQ0()); + }; + } + + public static void setNewValue(Load load, String loadField, @NotNull String newValue) { + LoadField field = LoadField.valueOf(loadField); + switch (field) { + case LOAD_TYPE -> load.setLoadType(LoadType.valueOf(newValue)); + case ACTIVE_POWER -> modifyP0(load, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), null); + case REACTIVE_POWER -> modifyQ0(load, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), null); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/ShuntCompensatorField.java b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/ShuntCompensatorField.java new file mode 100644 index 0000000..479b912 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/ShuntCompensatorField.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.equipmentfield; + +import com.powsybl.iidm.network.ShuntCompensator; +import com.powsybl.iidm.network.ShuntCompensatorLinearModel; +import com.powsybl.iidm.network.ShuntCompensatorModelType; +import com.powsybl.iidm.network.VoltageLevel; +import jakarta.validation.constraints.NotNull; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.AttributeModification; +import org.gridsuite.modification.dto.OperationType; +import org.gridsuite.modification.dto.ShuntCompensatorType; + +import static org.gridsuite.modification.modifications.ShuntCompensatorModification.*; + +/** + * @author Seddik Yengui + */ +public enum ShuntCompensatorField { + MAXIMUM_SECTION_COUNT, + SECTION_COUNT, + MAXIMUM_SUSCEPTANCE, + MAXIMUM_Q_AT_NOMINAL_VOLTAGE; + + public static String getReferenceValue(ShuntCompensator shuntCompensator, String shuntCompensatorField) { + VoltageLevel voltageLevel = shuntCompensator.getTerminal().getVoltageLevel(); + ShuntCompensatorField field = ShuntCompensatorField.valueOf(shuntCompensatorField); + return switch (field) { + case MAXIMUM_SECTION_COUNT -> String.valueOf(shuntCompensator.getMaximumSectionCount()); + case SECTION_COUNT -> String.valueOf(shuntCompensator.getSectionCount()); + case MAXIMUM_SUSCEPTANCE -> String.valueOf(shuntCompensator.getB() * shuntCompensator.getMaximumSectionCount()); + case MAXIMUM_Q_AT_NOMINAL_VOLTAGE -> String.valueOf(Math.abs(Math.pow(voltageLevel.getNominalV(), 2) * shuntCompensator.getB()) * shuntCompensator.getMaximumSectionCount()); + }; + } + + public static void setNewValue(ShuntCompensator shuntCompensator, String shuntCompensatorField, @NotNull String newValue) { + if (shuntCompensator.getModelType() != ShuntCompensatorModelType.LINEAR) { + throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, + String.format("Shunt compensator with %s model is not supported", shuntCompensator.getModelType())); + } + ShuntCompensatorLinearModel model = shuntCompensator.getModel(ShuntCompensatorLinearModel.class); + ShuntCompensatorField field = ShuntCompensatorField.valueOf(shuntCompensatorField); + VoltageLevel voltageLevel = shuntCompensator.getTerminal().getVoltageLevel(); + var shuntCompensatorType = ShuntCompensatorType.REACTOR; + if (model != null && model.getBPerSection() > 0) { + shuntCompensatorType = ShuntCompensatorType.CAPACITOR; + } + switch (field) { + case MAXIMUM_SECTION_COUNT -> modifyMaximumSectionCount(new AttributeModification<>((int) Double.parseDouble(newValue), OperationType.SET), + null, null, null, shuntCompensator, model); + case SECTION_COUNT -> modifySectionCount(new AttributeModification<>((int) Double.parseDouble(newValue), OperationType.SET), null, shuntCompensator); + case MAXIMUM_SUSCEPTANCE -> modifyMaxSusceptance(new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), + shuntCompensator.getMaximumSectionCount(), null, model); + case MAXIMUM_Q_AT_NOMINAL_VOLTAGE -> modifyMaximumQAtNominalVoltage(new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), + voltageLevel, shuntCompensator.getMaximumSectionCount(), null, model, shuntCompensatorType); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/TwoWindingsTransformerField.java b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/TwoWindingsTransformerField.java new file mode 100644 index 0000000..25b262d --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/TwoWindingsTransformerField.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.equipmentfield; + +import com.powsybl.iidm.network.PhaseTapChanger; +import com.powsybl.iidm.network.RatioTapChanger; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import jakarta.validation.constraints.NotNull; +import org.gridsuite.modification.dto.AttributeModification; +import org.gridsuite.modification.dto.OperationType; + +import static org.gridsuite.modification.modifications.TwoWindingsTransformerModification.*; + +/** + * @author Seddik Yengui + */ +public enum TwoWindingsTransformerField { + R, + X, + G, + B, + RATED_U1, + RATED_U2, + RATED_S, + TARGET_V, + RATIO_LOW_TAP_POSITION, + RATIO_TAP_POSITION, + RATIO_TARGET_DEADBAND, + REGULATION_VALUE, + PHASE_LOW_TAP_POSITION, + PHASE_TAP_POSITION, + PHASE_TARGET_DEADBAND; + + public static String getReferenceValue(TwoWindingsTransformer transformer, String twoWindingsTransformerField) { + TwoWindingsTransformerField field = TwoWindingsTransformerField.valueOf(twoWindingsTransformerField); + final PhaseTapChanger phaseTapChanger = transformer.getPhaseTapChanger(); + final RatioTapChanger ratioTapChanger = transformer.getRatioTapChanger(); + return switch (field) { + case R -> String.valueOf(transformer.getR()); + case X -> String.valueOf(transformer.getX()); + case G -> String.valueOf(transformer.getG()); + case B -> String.valueOf(transformer.getB()); + case RATED_U1 -> String.valueOf(transformer.getRatedU1()); + case RATED_U2 -> String.valueOf(transformer.getRatedU2()); + case RATED_S -> String.valueOf(transformer.getRatedS()); + case TARGET_V -> ratioTapChanger != null ? String.valueOf(ratioTapChanger.getTargetV()) : null; + case RATIO_LOW_TAP_POSITION -> ratioTapChanger != null ? String.valueOf(ratioTapChanger.getLowTapPosition()) : null; + case RATIO_TAP_POSITION -> ratioTapChanger != null ? String.valueOf(ratioTapChanger.getTapPosition()) : null; + case RATIO_TARGET_DEADBAND -> ratioTapChanger != null ? String.valueOf(ratioTapChanger.getTargetDeadband()) : null; + case REGULATION_VALUE -> phaseTapChanger != null ? String.valueOf(phaseTapChanger.getRegulationValue()) : null; + case PHASE_LOW_TAP_POSITION -> phaseTapChanger != null ? String.valueOf(phaseTapChanger.getLowTapPosition()) : null; + case PHASE_TAP_POSITION -> phaseTapChanger != null ? String.valueOf(phaseTapChanger.getTapPosition()) : null; + case PHASE_TARGET_DEADBAND -> phaseTapChanger != null ? String.valueOf(phaseTapChanger.getTargetDeadband()) : null; + }; + } + + public static void setNewValue(TwoWindingsTransformer transformer, String twoWindingsTransformerField, @NotNull String newValue) { + TwoWindingsTransformerField field = TwoWindingsTransformerField.valueOf(twoWindingsTransformerField); + final PhaseTapChanger phaseTapChanger = transformer.getPhaseTapChanger(); + final RatioTapChanger ratioTapChanger = transformer.getRatioTapChanger(); + final AttributeModification attributeModification = new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET); + + switch (field) { + case R -> modifyR(transformer, attributeModification, null); + case X -> modifyX(transformer, attributeModification, null); + case G -> modifyG(transformer, attributeModification, null); + case B -> modifyB(transformer, attributeModification, null); + case RATED_U1 -> modifyRatedU1(transformer, attributeModification, null); + case RATED_U2 -> modifyRatedU2(transformer, attributeModification, null); + case RATED_S -> modifyRatedS(transformer, attributeModification, null); + case TARGET_V -> modifyTargets(ratioTapChanger, null, true, attributeModification, null, null); + case RATIO_LOW_TAP_POSITION -> processTapChangerPositionsAndSteps(ratioTapChanger, null, true, + new AttributeModification<>((int) Double.parseDouble(newValue), OperationType.SET), null, null, null); + case RATIO_TAP_POSITION -> processTapChangerPositionsAndSteps(ratioTapChanger, null, true, + null, new AttributeModification<>((int) Double.parseDouble(newValue), OperationType.SET), null, null); + case RATIO_TARGET_DEADBAND -> modifyTargets(ratioTapChanger, null, true, null, attributeModification, null); + case REGULATION_VALUE -> processPhaseTapRegulation( + phaseTapChanger, null, true, null, attributeModification, null, null); + case PHASE_LOW_TAP_POSITION -> processTapChangerPositionsAndSteps(phaseTapChanger, null, true, + new AttributeModification<>((int) Double.parseDouble(newValue), OperationType.SET), null, null, null); + case PHASE_TAP_POSITION -> processTapChangerPositionsAndSteps(phaseTapChanger, null, true, + null, new AttributeModification<>((int) Double.parseDouble(newValue), OperationType.SET), null, null); + case PHASE_TARGET_DEADBAND -> processPhaseTapRegulation( + phaseTapChanger, null, true, null, null, attributeModification, null + ); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/VoltageLevelField.java b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/VoltageLevelField.java new file mode 100644 index 0000000..3d292b7 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/equipmentfield/VoltageLevelField.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.equipmentfield; + +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.iidm.network.extensions.IdentifiableShortCircuit; +import jakarta.validation.constraints.NotNull; +import org.gridsuite.modification.dto.AttributeModification; +import org.gridsuite.modification.dto.OperationType; + +import static org.gridsuite.modification.modifications.VoltageLevelModification.*; + +/** + * @author Seddik Yengui + */ +public enum VoltageLevelField { + NOMINAL_VOLTAGE, + LOW_VOLTAGE_LIMIT, + HIGH_VOLTAGE_LIMIT, + LOW_SHORT_CIRCUIT_CURRENT_LIMIT, + HIGH_SHORT_CIRCUIT_CURRENT_LIMIT; + + public static String getReferenceValue(VoltageLevel voltageLevel, String voltageLevelField) { + IdentifiableShortCircuit identifiableShortCircuit = voltageLevel.getExtension(IdentifiableShortCircuit.class); + VoltageLevelField field = VoltageLevelField.valueOf(voltageLevelField); + return switch (field) { + case NOMINAL_VOLTAGE -> String.valueOf(voltageLevel.getNominalV()); + case LOW_VOLTAGE_LIMIT -> String.valueOf(voltageLevel.getLowVoltageLimit()); + case HIGH_VOLTAGE_LIMIT -> String.valueOf(voltageLevel.getHighVoltageLimit()); + case LOW_SHORT_CIRCUIT_CURRENT_LIMIT -> identifiableShortCircuit != null ? String.valueOf(identifiableShortCircuit.getIpMin()) : null; + case HIGH_SHORT_CIRCUIT_CURRENT_LIMIT -> identifiableShortCircuit != null ? String.valueOf(identifiableShortCircuit.getIpMax()) : null; + }; + } + + public static void setNewValue(VoltageLevel voltageLevel, String voltageLevelField, @NotNull String newValue) { + VoltageLevelField field = VoltageLevelField.valueOf(voltageLevelField); + switch (field) { + case NOMINAL_VOLTAGE -> modifyNominalV(voltageLevel, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), null); + case LOW_VOLTAGE_LIMIT -> modifLowVoltageLimit(voltageLevel, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), null); + case HIGH_VOLTAGE_LIMIT -> modifyHighVoltageLimit(voltageLevel, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), null); + case LOW_SHORT_CIRCUIT_CURRENT_LIMIT -> modifyVoltageLevelShortCircuit( + new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), null, null, voltageLevel); + case HIGH_SHORT_CIRCUIT_CURRENT_LIMIT -> modifyVoltageLevelShortCircuit( + null, new AttributeModification<>(Double.parseDouble(newValue), OperationType.SET), null, voltageLevel); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/formula/FormulaInfos.java b/src/main/java/org/gridsuite/modification/dto/byfilter/formula/FormulaInfos.java new file mode 100644 index 0000000..23b7066 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/formula/FormulaInfos.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.formula; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.dto.byfilter.AbstractAssignmentInfos; + +/** + * @author Seddik Yengui + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +public class FormulaInfos extends AbstractAssignmentInfos { + + @Schema(description = "First reference field or value") + private ReferenceFieldOrValue fieldOrValue1; + + @Schema(description = "Second reference field or value") + private ReferenceFieldOrValue fieldOrValue2; + + @Schema(description = "Operator") + private Operator operator; + +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/formula/Operator.java b/src/main/java/org/gridsuite/modification/dto/byfilter/formula/Operator.java new file mode 100644 index 0000000..afe5da3 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/formula/Operator.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.formula; + +/** + * @author Seddik Yengui + */ + +public enum Operator { + ADDITION, + SUBTRACTION, + MULTIPLICATION, + DIVISION, + PERCENTAGE +} diff --git a/src/main/java/org/gridsuite/modification/dto/byfilter/formula/ReferenceFieldOrValue.java b/src/main/java/org/gridsuite/modification/dto/byfilter/formula/ReferenceFieldOrValue.java new file mode 100644 index 0000000..bf12187 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/dto/byfilter/formula/ReferenceFieldOrValue.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.dto.byfilter.formula; + +import com.powsybl.iidm.network.Identifiable; +import lombok.*; +import org.gridsuite.modification.NetworkModificationException; + +import static org.gridsuite.modification.dto.byfilter.equipmentfield.FieldUtils.getFieldValue; + + +/** + * @author Seddik Yengui + */ + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class ReferenceFieldOrValue { + private String equipmentField; + + private Double value; + + public Double getRefOrValue(Identifiable identifiable) { + if (value == null && equipmentField == null) { + throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, + "There is no value or reference to any of the equipment fields"); + } + + if (value != null && !Double.isNaN(value)) { + return value; + } + + String referenceValue = getFieldValue(identifiable, equipmentField); + + if (referenceValue == null) { + return Double.NaN; + } + + return Double.parseDouble(referenceValue); + } + +} diff --git a/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java b/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java new file mode 100644 index 0000000..3b59806 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java @@ -0,0 +1,234 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import com.powsybl.iidm.network.extensions.ConnectablePositionAdder; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.BranchModificationInfos; +import org.gridsuite.modification.dto.CurrentLimitsModificationInfos; +import org.gridsuite.modification.dto.CurrentTemporaryLimitModificationInfos; +import org.gridsuite.modification.dto.TemporaryLimitModificationType; +import org.gridsuite.modification.utils.ModificationUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.gridsuite.modification.NetworkModificationException.Type.BRANCH_MODIFICATION_ERROR; +import static org.gridsuite.modification.utils.ModificationUtils.insertReportNode; + +/** + * @author Florent MILLOT + */ +public abstract class AbstractBranchModification extends AbstractModification { + + private static final String DURATION = "duration"; + private static final String NAME = "name"; + protected final BranchModificationInfos modificationInfos; + + protected AbstractBranchModification(BranchModificationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + protected void modifyBranch(Branch branch, BranchModificationInfos branchModificationInfos, ReportNode subReportNode, String reporterKey, String reporterDefaultMessage) { + subReportNode.newReportNode() + .withMessageTemplate(reporterKey, reporterDefaultMessage) + .withUntypedValue("id", branchModificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + if (branchModificationInfos.getEquipmentName() != null) { + insertReportNode(subReportNode, ModificationUtils.getInstance().buildModificationReport(Optional.of(branch.getOptionalName()).orElse(null), branchModificationInfos.getEquipmentName().getValue(), "Name", 0)); + branch.setName(branchModificationInfos.getEquipmentName().getValue()); + } + + modifyBranchConnectivityAttributes(branchModificationInfos, branch, subReportNode); + + if (characteristicsModified(branchModificationInfos)) { + modifyCharacteristics(branch, branchModificationInfos, subReportNode); + } + + CurrentLimitsModificationInfos currentLimitsInfos1 = modificationInfos.getCurrentLimits1(); + CurrentLimitsModificationInfos currentLimitsInfos2 = modificationInfos.getCurrentLimits2(); + List side1LimitsReports = new ArrayList<>(); + CurrentLimits currentLimits1 = branch.getCurrentLimits1().orElse(null); + if (currentLimitsInfos1 != null) { + modifyCurrentLimits(currentLimitsInfos1, branch.newCurrentLimits1(), currentLimits1, side1LimitsReports); + } + List side2LimitsReports = new ArrayList<>(); + CurrentLimits currentLimits2 = branch.getCurrentLimits2().orElse(null); + if (currentLimitsInfos2 != null) { + modifyCurrentLimits(currentLimitsInfos2, branch.newCurrentLimits2(), currentLimits2, side2LimitsReports); + } + if (!side1LimitsReports.isEmpty() || !side2LimitsReports.isEmpty()) { + ReportNode limitsReportNode = subReportNode.newReportNode().withMessageTemplate("limits", "Limits").add(); + ModificationUtils.getInstance().reportModifications(limitsReportNode, side1LimitsReports, "side1LimitsModification", + " Side 1"); + ModificationUtils.getInstance().reportModifications(limitsReportNode, side2LimitsReports, "side2LimitsModification", + " Side 2"); + } + + updateConnections(branch, branchModificationInfos); + } + + private void updateConnections(Branch branch, BranchModificationInfos branchModificationInfos) { + List errorSides = new ArrayList<>(); + List errorTypes = new ArrayList<>(); + if (branchModificationInfos.getTerminal1Connected() != null && !updateConnection(branch, TwoSides.ONE, modificationInfos.getTerminal1Connected().getValue())) { + errorSides.add(TwoSides.ONE); + errorTypes.add(Boolean.TRUE.equals(modificationInfos.getTerminal1Connected().getValue()) ? "connect" : "disconnect"); + } + if (branchModificationInfos.getTerminal2Connected() != null && !updateConnection(branch, TwoSides.TWO, modificationInfos.getTerminal2Connected().getValue())) { + errorSides.add(TwoSides.TWO); + errorTypes.add(Boolean.TRUE.equals(modificationInfos.getTerminal2Connected().getValue()) ? "connect" : "disconnect"); + } + if (!errorSides.isEmpty()) { + throw new NetworkModificationException(BRANCH_MODIFICATION_ERROR, + String.format("Could not %s equipment '%s' on side %s", + errorTypes.stream().distinct().collect(Collectors.joining("/")), + branch.getId(), + errorSides.stream().map(Enum::toString).collect(Collectors.joining("/")))); + } + } + + private boolean updateConnection(Branch branch, TwoSides side, Boolean connectionChange) { + boolean done = true; + if (branch.getTerminal(side).isConnected() && Boolean.FALSE.equals(connectionChange)) { + branch.getTerminal(side).disconnect(); + if (branch.getTerminal(side).isConnected()) { + done = false; + } + } else if (!branch.getTerminal(side).isConnected() && Boolean.TRUE.equals(connectionChange)) { + branch.getTerminal(side).connect(); + if (!branch.getTerminal(side).isConnected()) { + done = false; + } + } + return done; + } + + protected void modifyCurrentLimits(CurrentLimitsModificationInfos currentLimitsInfos, CurrentLimitsAdder limitsAdder, CurrentLimits currentLimits, List limitsReports) { + boolean hasPermanent = currentLimitsInfos.getPermanentLimit() != null; + if (hasPermanent) { + limitsReports.add(ModificationUtils.getInstance().buildModificationReport(currentLimits != null ? currentLimits.getPermanentLimit() : Double.NaN, + currentLimitsInfos.getPermanentLimit(), "IST", 2)); + limitsAdder.setPermanentLimit(currentLimitsInfos.getPermanentLimit()); + } else { + if (currentLimits != null) { + limitsAdder.setPermanentLimit(currentLimits.getPermanentLimit()); + } + } + modifyTemporaryLimits(currentLimitsInfos, limitsAdder, currentLimits, limitsReports); + limitsAdder.add(); + } + + protected void modifyTemporaryLimits(CurrentLimitsModificationInfos currentLimitsInfos, CurrentLimitsAdder limitsAdder, + CurrentLimits currentLimits, List limitsReports) { + // we create a mutable list of temporary limits to be able to remove the limits that are modified in current modification + List branchTemporaryLimits = new ArrayList<>(); + if (currentLimits != null) { + branchTemporaryLimits.addAll(currentLimits.getTemporaryLimits()); + } + List temporaryLimitsReports = new ArrayList<>(); + for (CurrentTemporaryLimitModificationInfos limit : currentLimitsInfos.getTemporaryLimits()) { + int limitAcceptableDuration = limit.getAcceptableDuration() == null ? Integer.MAX_VALUE : limit.getAcceptableDuration(); + double limitValue = limit.getValue() == null ? Double.MAX_VALUE : limit.getValue(); + String limitDurationToReport = limitAcceptableDuration == Integer.MAX_VALUE ? " " : String.valueOf(limitAcceptableDuration); + String limitValueToReport = limitValue == Double.MAX_VALUE ? "no value" : String.valueOf(limitValue); + LoadingLimits.TemporaryLimit limitToModify = null; + if (currentLimits != null) { + limitToModify = currentLimits.getTemporaryLimit(limitAcceptableDuration); + if (limitToModify != null && !limitToModify.getName().equals(limit.getName())) { + throw new PowsyblException("2temporary limits have the same duration " + limitAcceptableDuration); + } + // we remove the limit to modify from the list of temporary limits so we can get the list of temporary limits comming from previous modifications + branchTemporaryLimits.removeIf(temporaryLimit -> temporaryLimit.getAcceptableDuration() == limitAcceptableDuration); + } + if (limitToModify == null && limit.getModificationType() == TemporaryLimitModificationType.ADDED) { + temporaryLimitsReports.add(ReportNode.newRootReportNode() + .withMessageTemplate("temporaryLimitAdded" + limit.getName(), " ${name} (${duration}) added with ${value}") + .withUntypedValue(NAME, limit.getName()) + .withUntypedValue(DURATION, limitDurationToReport) + .withUntypedValue("value", limitValueToReport) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + + } else if (limitToModify != null) { + if (limit.getModificationType() == TemporaryLimitModificationType.DELETED) { + temporaryLimitsReports.add(ReportNode.newRootReportNode() + .withMessageTemplate("temporaryLimitDeleted" + limit.getName(), " ${name} (${duration}) deleted") + .withUntypedValue(NAME, limit.getName()) + .withUntypedValue(DURATION, limitDurationToReport) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + continue; + } else if (Double.compare(limitToModify.getValue(), limitValue) != 0 && limit.getModificationType() != null) { + temporaryLimitsReports.add(ReportNode.newRootReportNode() + .withMessageTemplate("temporaryLimitModified" + limit.getName(), " ${name} (${duration}) : ${oldValue} -> ${value}") + .withUntypedValue(NAME, limit.getName()) + .withUntypedValue(DURATION, limitDurationToReport) + .withUntypedValue("value", limitValueToReport) + .withUntypedValue("oldValue", + limitToModify.getValue() == Double.MAX_VALUE ? "no value" + : String.valueOf(limitToModify.getValue())) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + } else { + limitValue = limitToModify.getValue(); + } + } else { + continue; + } + limitsAdder + .beginTemporaryLimit() + .setName(limit.getName()) + .setValue(limitValue) + .setAcceptableDuration(limitAcceptableDuration) + .endTemporaryLimit(); + } + // we add the temporary limits comming from previous modifications + if (!branchTemporaryLimits.isEmpty()) { + for (LoadingLimits.TemporaryLimit limit : branchTemporaryLimits) { + limitsAdder + .beginTemporaryLimit() + .setName(limit.getName()) + .setValue(limit.getValue()) + .setAcceptableDuration(limit.getAcceptableDuration()) + .endTemporaryLimit(); + } + } + if (!temporaryLimitsReports.isEmpty()) { + temporaryLimitsReports.add(0, ReportNode.newRootReportNode() + .withMessageTemplate("temporaryLimitsModification", " Temporary current limits :") + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + limitsReports.addAll(temporaryLimitsReports); + } + } + + protected boolean characteristicsModified(BranchModificationInfos branchModificationInfos) { + return branchModificationInfos.getX() != null + && branchModificationInfos.getX().getValue() != null + || branchModificationInfos.getR() != null + && branchModificationInfos.getR().getValue() != null; + } + + protected abstract void modifyCharacteristics(Branch branch, BranchModificationInfos branchModificationInfos, + ReportNode subReportNode); + + private ReportNode modifyBranchConnectivityAttributes(BranchModificationInfos branchModificationInfos, + Branch branch, ReportNode subReportNode) { + ConnectablePosition connectablePosition = (ConnectablePosition) branch.getExtension(ConnectablePosition.class); + ConnectablePositionAdder connectablePositionAdder = branch.newExtension(ConnectablePositionAdder.class); + return ModificationUtils.getInstance().modifyBranchConnectivityAttributes(connectablePosition, connectablePositionAdder, branch, branchModificationInfos, subReportNode); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/AbstractModification.java b/src/main/java/org/gridsuite/modification/modifications/AbstractModification.java new file mode 100644 index 0000000..88a1d3c --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/AbstractModification.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import org.gridsuite.modification.IFilterService; +import org.gridsuite.modification.NetworkModificationException; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.computation.ComputationManager; +import com.powsybl.iidm.modification.AbstractNetworkModification; +import com.powsybl.iidm.modification.topology.NamingStrategy; +import com.powsybl.iidm.network.Network; + +/** + * @author Slimane Amar + */ +public abstract class AbstractModification extends AbstractNetworkModification { + + public static final String CHARACTERISTICS = "Characteristics"; + public static final String SETPOINTS = "Setpoints"; + + @Override + public void apply(Network network, NamingStrategy namingStrategy, boolean throwException, ComputationManager computationManager, ReportNode reportNode) { + apply(network, reportNode); + } + + public void check(Network network) throws NetworkModificationException { + // To perform input data check before hypothesis apply. Nothing to check here + } + + public void initApplicationContext(IFilterService filterService) { + // To add some specific information + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/AbstractScaling.java b/src/main/java/org/gridsuite/modification/modifications/AbstractScaling.java new file mode 100644 index 0000000..f99ac66 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/AbstractScaling.java @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.modification.scalable.Scalable; +import com.powsybl.iidm.network.Network; + +import org.gridsuite.modification.IFilterService; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.*; +import org.gridsuite.modification.utils.ModificationUtils; +import org.springframework.util.CollectionUtils; + +import static org.gridsuite.modification.utils.ModificationUtils.createReport; +import static org.gridsuite.modification.utils.ModificationUtils.distinctByKey; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +/** + * @author bendaamerahm + */ +public abstract class AbstractScaling extends AbstractModification { + protected final ScalingInfos scalingInfos; + + protected IFilterService filterService; + + protected AbstractScaling(ScalingInfos scalingInfos) { + this.scalingInfos = scalingInfos; + } + + @Override + public void initApplicationContext(IFilterService filterService) { + this.filterService = filterService; + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + // collect all filters from all variations + var filters = scalingInfos.getVariations().stream() + .flatMap(v -> v.getFilters().stream()) + .filter(distinctByKey(FilterInfos::getId)) + .collect(Collectors.toMap(FilterInfos::getId, FilterInfos::getName)); + + Map exportFilters = ModificationUtils.getUuidFilterEquipmentsMap(filterService, network, subReportNode, filters, scalingInfos.getErrorType()); + if (exportFilters != null) { + Map filtersWithWrongEquipmentIds = ModificationUtils.getUuidFilterWrongEquipmentsIdsMap(subReportNode, exportFilters, filters); + + // apply variations + scalingInfos.getVariations().forEach(variation -> { + List identifiableAttributes = ModificationUtils.getIdentifiableAttributes(exportFilters, filtersWithWrongEquipmentIds, variation.getFilters(), subReportNode); + + if (CollectionUtils.isEmpty(identifiableAttributes)) { + String filterNames = variation.getFilters().stream().map(FilterInfos::getName).collect(Collectors.joining(", ")); + createReport(subReportNode, + "allFiltersWrong", + "All of the following variation's filters have equipments with wrong id : ${filterNames}", + Map.of("filterNames", filterNames), TypedValue.WARN_SEVERITY); + } else { + applyVariation(network, subReportNode, identifiableAttributes, variation); + } + }); + createReport(subReportNode, "scalingCreated", "new scaling created", Map.of(), TypedValue.INFO_SEVERITY); + } + } + + private void applyVariation(Network network, + ReportNode subReportNode, + List identifiableAttributes, + ScalingVariationInfos variation) { + switch (variation.getVariationMode()) { + case PROPORTIONAL: + applyProportionalVariation(network, subReportNode, identifiableAttributes, variation); + break; + case PROPORTIONAL_TO_PMAX: + applyProportionalToPmaxVariation(network, subReportNode, identifiableAttributes, variation); + break; + case REGULAR_DISTRIBUTION: + applyRegularDistributionVariation(network, subReportNode, identifiableAttributes, variation); + break; + case VENTILATION: + applyVentilationVariation(network, subReportNode, identifiableAttributes, variation, getDistributionKeys(identifiableAttributes, subReportNode)); + break; + case STACKING_UP: + applyStackingUpVariation(network, subReportNode, identifiableAttributes, variation); + break; + default: + throw new NetworkModificationException(scalingInfos.getErrorType(), String.format("This variation mode is not supported : %s", variation.getVariationMode().name())); + } + } + + private Double getDistributionKeys(List identifiableAttributes, ReportNode subReportNode) { + var distributionKeys = identifiableAttributes.stream() + .filter(equipment -> equipment.getDistributionKey() != null) + .mapToDouble(IdentifiableAttributes::getDistributionKey) + .sum(); + if (distributionKeys == 0) { + createReport(subReportNode, "distributionKeysNotFound", "This mode is available only for equipment with distribution key", Map.of(), TypedValue.WARN_SEVERITY); + return null; + } + return distributionKeys; + } + + protected abstract void applyStackingUpVariation(Network network, ReportNode subReportNode, List identifiableAttributes, ScalingVariationInfos scalingVariationInfos); + + protected abstract void applyVentilationVariation(Network network, ReportNode subReportNode, List identifiableAttributes, ScalingVariationInfos scalingVariationInfos, Double distributionKeys); + + protected abstract void applyRegularDistributionVariation(Network network, ReportNode subReportNode, List identifiableAttributes, ScalingVariationInfos scalingVariationInfos); + + protected abstract void applyProportionalToPmaxVariation(Network network, ReportNode subReportNode, List identifiableAttributes, ScalingVariationInfos scalingVariationInfos); + + protected abstract void applyProportionalVariation(Network network, ReportNode subReportNode, List identifiableAttributes, ScalingVariationInfos scalingVariationInfos); + + protected abstract double getAsked(ScalingVariationInfos variationInfos, AtomicReference sum); + + protected abstract Scalable getScalable(String id); + +} diff --git a/src/main/java/org/gridsuite/modification/modifications/BatteryCreation.java b/src/main/java/org/gridsuite/modification/modifications/BatteryCreation.java new file mode 100644 index 0000000..81dd70b --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/BatteryCreation.java @@ -0,0 +1,178 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.BatteryCreationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import java.util.ArrayList; +import java.util.List; + +import static org.gridsuite.modification.NetworkModificationException.Type.BATTERY_ALREADY_EXISTS; +import static org.gridsuite.modification.NetworkModificationException.Type.CREATE_BATTERY_ERROR; +import static org.gridsuite.modification.modifications.BatteryModification.ERROR_MESSAGE; +import static org.gridsuite.modification.utils.ModificationUtils.*; + +/** + * @author Ghazwa Rehili + */ +public class BatteryCreation extends AbstractModification { + + private final BatteryCreationInfos modificationInfos; + private static final String LIMITS = "Limits"; + private static final String ACTIVE_LIMITS = "Active limits"; + + public BatteryCreation(BatteryCreationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (network.getBattery(modificationInfos.getEquipmentId()) != null) { + throw new NetworkModificationException(BATTERY_ALREADY_EXISTS, modificationInfos.getEquipmentId()); + } + + // check connectivity + ModificationUtils.getInstance() + .controlConnectivity(network, modificationInfos.getVoltageLevelId(), + modificationInfos.getBusOrBusbarSectionId(), modificationInfos.getConnectionPosition()); + + // check reactive limits + ModificationUtils.getInstance().checkReactiveLimitsCreation(modificationInfos, + modificationInfos.getErrorType(), + modificationInfos.getEquipmentId(), + "Battery"); + + ModificationUtils.getInstance().checkActivePowerControl(modificationInfos.getParticipate(), + modificationInfos.getDroop(), CREATE_BATTERY_ERROR, String.format(ERROR_MESSAGE, modificationInfos.getEquipmentId())); + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + // create the battery in the network + VoltageLevel voltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId()); + if (voltageLevel.getTopologyKind() == TopologyKind.NODE_BREAKER) { + createBatteryInNodeBreaker(voltageLevel, modificationInfos, network, subReportNode); + } else { + createBatteryInBusBreaker(voltageLevel, modificationInfos, subReportNode); + } + if (!modificationInfos.isTerminalConnected()) { + network.getBattery(modificationInfos.getEquipmentId()).getTerminal().disconnect(); + } + // properties + Battery battery = network.getBattery(modificationInfos.getEquipmentId()); + PropertiesUtils.applyProperties(battery, subReportNode, modificationInfos.getProperties(), "BatteryProperties"); + } + + private void createBatteryInNodeBreaker(VoltageLevel voltageLevel, BatteryCreationInfos batteryCreationInfos, Network network, ReportNode subReportNode) { + BatteryAdder batteryAdder = createBatteryAdderInNodeBreaker(voltageLevel, batteryCreationInfos); + createInjectionInNodeBreaker(voltageLevel, batteryCreationInfos, network, batteryAdder, subReportNode); + var battery = ModificationUtils.getInstance().getBattery(network, batteryCreationInfos.getEquipmentId()); + addExtensionsToBattery(batteryCreationInfos, battery, subReportNode); + } + + private BatteryAdder createBatteryAdderInNodeBreaker(VoltageLevel voltageLevel, BatteryCreationInfos batteryCreationInfos) { + + return voltageLevel.newBattery() + .setId(batteryCreationInfos.getEquipmentId()) + .setName(batteryCreationInfos.getEquipmentName()) + .setMinP(batteryCreationInfos.getMinP()) + .setMaxP(batteryCreationInfos.getMaxP()) + .setTargetP(batteryCreationInfos.getTargetP()) + .setTargetQ(nanIfNull(batteryCreationInfos.getTargetQ())); + } + + private void createBatteryInBusBreaker(VoltageLevel voltageLevel, BatteryCreationInfos batteryCreationInfos, ReportNode subReportNode) { + Bus bus = ModificationUtils.getInstance().getBusBreakerBus(voltageLevel, batteryCreationInfos.getBusOrBusbarSectionId()); + + // creating the battery + Battery battery = voltageLevel.newBattery() + .setBus(bus.getId()) + .setConnectableBus(bus.getId()) + .setId(batteryCreationInfos.getEquipmentId()) + .setName(batteryCreationInfos.getEquipmentName()) + .setMinP(batteryCreationInfos.getMinP()) + .setMaxP(batteryCreationInfos.getMaxP()) + .setTargetP(batteryCreationInfos.getTargetP()) + .setTargetQ(nanIfNull(batteryCreationInfos.getTargetQ())) + .add(); + + addExtensionsToBattery(batteryCreationInfos, battery, subReportNode); + + subReportNode.newReportNode() + .withMessageTemplate("batteryCreated", "New battery with id=${id} created") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + + private void addExtensionsToBattery(BatteryCreationInfos batteryCreationInfos, Battery battery, ReportNode subReportNode) { + if (batteryCreationInfos.getEquipmentName() != null) { + ModificationUtils.getInstance().reportElementaryCreation(subReportNode, batteryCreationInfos.getEquipmentName(), "Name"); + } + reportInjectionCreationConnectivity(batteryCreationInfos, subReportNode); + ReportNode subReportNodeLimits = reportBatteryActiveLimits(batteryCreationInfos, subReportNode); + ModificationUtils.getInstance().createReactiveLimits(batteryCreationInfos, battery, subReportNodeLimits); + ReportNode subReportNodeSetpoints = reportBatterySetPoints(batteryCreationInfos, subReportNode); + createBatteryActivePowerControl(batteryCreationInfos, battery, subReportNodeSetpoints); + } + + private ReportNode reportBatterySetPoints(BatteryCreationInfos batteryCreationInfos, ReportNode subReportNode) { + List setPointReports = new ArrayList<>(); + setPointReports.add(ModificationUtils.getInstance() + .buildCreationReport(batteryCreationInfos.getTargetP(), "Active power")); + if (batteryCreationInfos.getTargetQ() != null) { + setPointReports.add(ModificationUtils.getInstance() + .buildCreationReport(batteryCreationInfos.getTargetQ(), "Reactive power")); + } + return ModificationUtils.getInstance().reportModifications(subReportNode, setPointReports, "SetPointCreated", "Setpoints"); + } + + private ReportNode reportBatteryActiveLimits(BatteryCreationInfos batteryCreationInfos, ReportNode subReportNode) { + ReportNode subReportNodeLimits = subReportNode.newReportNode().withMessageTemplate(LIMITS, LIMITS).add(); + List limitsReports = new ArrayList<>(); + limitsReports.add(ModificationUtils.getInstance().buildCreationReport( + batteryCreationInfos.getMinP(), "Min active power")); + limitsReports.add(ModificationUtils.getInstance().buildCreationReport( + batteryCreationInfos.getMaxP(), "Max active power")); + ModificationUtils.getInstance().reportModifications(subReportNodeLimits, limitsReports, "ActiveLimitsCreated", ACTIVE_LIMITS); + return subReportNodeLimits; + } + + private void createBatteryActivePowerControl(BatteryCreationInfos batteryCreationInfos, Battery battery, ReportNode subReporter) { + if (batteryCreationInfos.getParticipate() != null && batteryCreationInfos.getDroop() != null) { + List activePowerRegulationReports = new ArrayList<>(); + try { + battery.newExtension(ActivePowerControlAdder.class) + .withParticipate(batteryCreationInfos.getParticipate()) + .withDroop(batteryCreationInfos.getDroop()) + .add(); + activePowerRegulationReports.add(ModificationUtils.getInstance().buildCreationReport( + batteryCreationInfos.getParticipate(), + "Participate")); + activePowerRegulationReports.add(ModificationUtils.getInstance().buildCreationReport( + batteryCreationInfos.getDroop(), + "Droop")); + } catch (PowsyblException e) { + activePowerRegulationReports.add(ReportNode.newRootReportNode() + .withMessageTemplate("ActivePowerExtensionAddError", "cannot add active power extension on battery with id=${id} : ${message}") + .withUntypedValue("id", batteryCreationInfos.getEquipmentId()) + .withUntypedValue("message", e.getMessage()) + .withSeverity(TypedValue.ERROR_SEVERITY) + .build()); + } + ModificationUtils.getInstance().reportModifications(subReporter, activePowerRegulationReports, "ActivePowerRegulationCreated", "Active power regulation"); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/BatteryModification.java b/src/main/java/org/gridsuite/modification/modifications/BatteryModification.java new file mode 100644 index 0000000..a30141a --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/BatteryModification.java @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.Battery; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ReactiveCapabilityCurve; +import com.powsybl.iidm.network.ReactiveCapabilityCurveAdder; +import com.powsybl.iidm.network.ReactiveLimitsKind; +import com.powsybl.iidm.network.extensions.ActivePowerControl; +import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import com.powsybl.iidm.network.extensions.ConnectablePositionAdder; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.AttributeModification; +import org.gridsuite.modification.dto.BatteryModificationInfos; +import org.gridsuite.modification.dto.ReactiveCapabilityCurveModificationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import java.util.Collection; +import java.util.List; + +import static org.gridsuite.modification.NetworkModificationException.Type.MODIFY_BATTERY_ERROR; +import static org.gridsuite.modification.utils.ModificationUtils.insertReportNode; + +/** + * @author Ghazwa Rehili + */ +public class BatteryModification extends AbstractModification { + + private final BatteryModificationInfos modificationInfos; + + private static final String LIMITS = "Limits"; + private static final String ACTIVE_LIMITS = "Active limits"; + private static final String SETPOINTS = "Setpoints"; + public static final String ERROR_MESSAGE = "Battery '%s' : "; + + public BatteryModification(BatteryModificationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (modificationInfos == null) { + throw new NetworkModificationException(MODIFY_BATTERY_ERROR, "Missing required attributes to modify the equipment"); + } + Battery battery = ModificationUtils.getInstance().getBattery(network, modificationInfos.getEquipmentId()); + String errorMessage = "Battery '" + modificationInfos.getEquipmentId() + "' : "; + ModificationUtils.getInstance().checkReactiveLimit(battery, modificationInfos.getMinQ(), modificationInfos.getMaxQ(), + modificationInfos.getReactiveCapabilityCurvePoints(), MODIFY_BATTERY_ERROR, errorMessage); + checkActivePowerZeroOrBetweenMinAndMaxActivePowerBattery(modificationInfos, battery, MODIFY_BATTERY_ERROR, errorMessage); + } + + private void checkActivePowerZeroOrBetweenMinAndMaxActivePowerBattery(BatteryModificationInfos modificationInfos, Battery battery, NetworkModificationException.Type exceptionType, String errorMessage) { + ModificationUtils.getInstance().checkActivePowerZeroOrBetweenMinAndMaxActivePower( + modificationInfos.getTargetP(), + modificationInfos.getMinP(), + modificationInfos.getMaxP(), + battery.getMinP(), + battery.getMaxP(), + battery.getTargetP(), + exceptionType, + errorMessage + ); + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + Battery battery = ModificationUtils.getInstance().getBattery(network, modificationInfos.getEquipmentId()); + // modify the battery in the network + modifyBattery(battery, modificationInfos, subReportNode); + } + + private void modifyBattery(Battery battery, BatteryModificationInfos modificationInfos, ReportNode subReportNode) { + subReportNode.newReportNode() + .withMessageTemplate("batteryModification", "Battery with id=${id} modified :") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + + if (modificationInfos.getEquipmentName() != null && modificationInfos.getEquipmentName().getValue() != null) { + ModificationUtils.getInstance().applyElementaryModifications(battery::setName, () -> battery.getOptionalName().orElse("No value"), modificationInfos.getEquipmentName(), subReportNode, "Name"); + } + + modifyBatteryLimitsAttributes(modificationInfos, battery, subReportNode); + modifyBatterySetpointsAttributes( + modificationInfos.getTargetP(), modificationInfos.getTargetQ(), + modificationInfos.getParticipate(), modificationInfos.getDroop(), + battery, subReportNode); + modifyBatteryConnectivityAttributes(modificationInfos, battery, subReportNode); + PropertiesUtils.applyProperties(battery, subReportNode, modificationInfos.getProperties(), "BatteryProperties"); + } + + public static void modifyBatterySetpointsAttributes(AttributeModification targetP, + AttributeModification targetQ, + AttributeModification participate, + AttributeModification droop, + Battery battery, + ReportNode subReportNode) { + ReportNode reportActivePower = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(battery::setTargetP, battery::getTargetP, targetP, "Active power"); + ReportNode reportReactivePower = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(battery::setTargetQ, battery::getTargetQ, targetQ, "Reactive power"); + ReportNode subReporterSetpoints = null; + if (subReportNode != null && (reportActivePower != null || reportReactivePower != null)) { + subReporterSetpoints = subReportNode.newReportNode().withMessageTemplate(SETPOINTS, SETPOINTS).add(); + if (reportActivePower != null) { + insertReportNode(subReporterSetpoints, reportActivePower); + } + if (reportReactivePower != null) { + insertReportNode(subReporterSetpoints, reportReactivePower); + } + } + modifyBatteryActivePowerControlAttributes(participate, droop, battery, subReportNode, subReporterSetpoints); + } + + private void modifyBatteryLimitsAttributes(BatteryModificationInfos modificationInfos, + Battery battery, ReportNode subReportNode) { + ReportNode subReportNodeLimits = modifyBatteryActiveLimitsAttributes(modificationInfos.getMaxP(), modificationInfos.getMinP(), battery, subReportNode); + modifyBatteryReactiveLimitsAttributes(modificationInfos, battery, subReportNode, subReportNodeLimits); + } + + private void modifyBatteryReactiveCapabilityCurvePoints(BatteryModificationInfos modificationInfos, + Battery battery, ReportNode subReportNode, ReportNode subReportNodeLimits) { + + ReactiveCapabilityCurveAdder adder = battery.newReactiveCapabilityCurve(); + List modificationPoints = modificationInfos.getReactiveCapabilityCurvePoints(); + Collection points = battery.getReactiveLimits().getKind() == ReactiveLimitsKind.CURVE ? battery.getReactiveLimits(ReactiveCapabilityCurve.class).getPoints() : List.of(); + ModificationUtils.getInstance().modifyReactiveCapabilityCurvePoints(points, modificationPoints, adder, subReportNode, subReportNodeLimits); + } + + public static ReportNode modifyBatteryActiveLimitsAttributes(AttributeModification maxP, + AttributeModification minP, + Battery battery, ReportNode subReportNode) { + ReportNode subReportNodeLimits = null; + ReportNode reportMaxActivePower = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(battery::setMaxP, battery::getMaxP, maxP, "Max active power"); + ReportNode reportMinActivePower = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(battery::setMinP, battery::getMinP, minP, "Min active power"); + if (subReportNode != null && (reportMaxActivePower != null || reportMinActivePower != null)) { + subReportNodeLimits = subReportNode.newReportNode().withMessageTemplate(LIMITS, LIMITS).add(); + ReportNode subReporterActiveLimits = subReportNodeLimits.newReportNode().withMessageTemplate(ACTIVE_LIMITS, ACTIVE_LIMITS).add(); + if (reportMaxActivePower != null) { + insertReportNode(subReporterActiveLimits, reportMaxActivePower); + } + if (reportMinActivePower != null) { + insertReportNode(subReporterActiveLimits, reportMinActivePower); + } + } + return subReportNodeLimits; + } + + private void modifyBatteryReactiveLimitsAttributes(BatteryModificationInfos modificationInfos, + Battery battery, ReportNode subReportNode, ReportNode subReportNodeLimits) { + + if (modificationInfos.getReactiveCapabilityCurve() != null) { + if (Boolean.TRUE.equals(modificationInfos.getReactiveCapabilityCurve().getValue() + && modificationInfos.getReactiveCapabilityCurvePoints() != null + && !modificationInfos.getReactiveCapabilityCurvePoints().isEmpty())) { + modifyBatteryReactiveCapabilityCurvePoints(modificationInfos, battery, subReportNode, subReportNodeLimits); + } else if (Boolean.FALSE.equals(modificationInfos.getReactiveCapabilityCurve().getValue())) { + ModificationUtils.getInstance().modifyMinMaxReactiveLimits(modificationInfos.getMinQ(), modificationInfos.getMaxQ(), battery, subReportNode, subReportNodeLimits); + } + } + } + + public static ReportNode modifyBatteryActivePowerControlAttributes(AttributeModification participate, + AttributeModification droop, + Battery battery, + ReportNode subReportNode, + ReportNode subReportNodeSetpoints) { + ActivePowerControl activePowerControl = battery.getExtension(ActivePowerControl.class); + ActivePowerControlAdder activePowerControlAdder = battery.newExtension(ActivePowerControlAdder.class); + return ModificationUtils.getInstance().modifyActivePowerControlAttributes(activePowerControl, activePowerControlAdder, + participate, droop, subReportNode, subReportNodeSetpoints, MODIFY_BATTERY_ERROR, String.format(ERROR_MESSAGE, battery.getId())); + } + + private ReportNode modifyBatteryConnectivityAttributes(BatteryModificationInfos modificationInfos, + Battery battery, ReportNode subReportNode) { + ConnectablePosition connectablePosition = battery.getExtension(ConnectablePosition.class); + ConnectablePositionAdder connectablePositionAdder = battery.newExtension(ConnectablePositionAdder.class); + return ModificationUtils.getInstance().modifyInjectionConnectivityAttributes(connectablePosition, connectablePositionAdder, battery, modificationInfos, subReportNode); + } +} + diff --git a/src/main/java/org/gridsuite/modification/modifications/BusbarSectionFinderTraverser.java b/src/main/java/org/gridsuite/modification/modifications/BusbarSectionFinderTraverser.java new file mode 100644 index 0000000..78043b3 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/BusbarSectionFinderTraverser.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.iidm.network.IdentifiableType; +import com.powsybl.iidm.network.Switch; +import com.powsybl.iidm.network.Terminal; +import com.powsybl.math.graph.TraverseResult; + +/** + * @author Slimane Amar + */ +// FIXME : to remove when this class is available in network-store +public class BusbarSectionFinderTraverser implements Terminal.TopologyTraverser { + + private final boolean onlyConnectedBbs; + + private String firstTraversedBbsId; + + public BusbarSectionFinderTraverser(boolean onlyConnectedBbs) { + this.onlyConnectedBbs = onlyConnectedBbs; + } + + @Override + public TraverseResult traverse(Terminal terminal, boolean connected) { + if (terminal.getConnectable().getType() == IdentifiableType.BUSBAR_SECTION) { + firstTraversedBbsId = terminal.getConnectable().getId(); + return TraverseResult.TERMINATE_TRAVERSER; + } + return TraverseResult.CONTINUE; + } + + @Override + public TraverseResult traverse(Switch aSwitch) { + if (onlyConnectedBbs && aSwitch.isOpen()) { + return TraverseResult.TERMINATE_PATH; + } + return TraverseResult.CONTINUE; + } + + public String getFirstTraversedBbsId() { + return firstTraversedBbsId; + } +} + diff --git a/src/main/java/org/gridsuite/modification/modifications/ByFilterDeletion.java b/src/main/java/org/gridsuite/modification/modifications/ByFilterDeletion.java new file mode 100644 index 0000000..4fee0dc --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/ByFilterDeletion.java @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.modification.topology.RemoveFeederBay; +import com.powsybl.iidm.modification.topology.RemoveHvdcLineBuilder; +import com.powsybl.iidm.modification.topology.RemoveSubstationBuilder; +import com.powsybl.iidm.modification.topology.RemoveVoltageLevel; +import com.powsybl.iidm.network.HvdcConverterStation; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.iidm.network.IdentifiableType; +import com.powsybl.iidm.network.Network; + +import org.gridsuite.modification.IFilterService; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.ByFilterDeletionInfos; +import org.gridsuite.modification.dto.FilterEquipments; +import org.gridsuite.modification.dto.FilterInfos; +import org.gridsuite.modification.dto.IdentifiableAttributes; +import org.gridsuite.modification.utils.ModificationUtils; +import org.springframework.util.CollectionUtils; + +import static org.gridsuite.modification.utils.ModificationUtils.createReport; +import static org.gridsuite.modification.utils.ModificationUtils.distinctByKey; + +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author Antoine Bouhours + */ +public class ByFilterDeletion extends AbstractModification { + + private final ByFilterDeletionInfos modificationInfos; + + protected IFilterService filterService; + + private static final EnumSet CONNECTABLE_TYPES = EnumSet.of( + IdentifiableType.LINE, + IdentifiableType.TWO_WINDINGS_TRANSFORMER, + IdentifiableType.THREE_WINDINGS_TRANSFORMER, + IdentifiableType.GENERATOR, + IdentifiableType.BATTERY, + IdentifiableType.LOAD, + IdentifiableType.SHUNT_COMPENSATOR, + IdentifiableType.DANGLING_LINE, + IdentifiableType.STATIC_VAR_COMPENSATOR + ); + + public ByFilterDeletion(ByFilterDeletionInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void initApplicationContext(IFilterService filterService) { + this.filterService = filterService; + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + var filters = modificationInfos.getFilters().stream() + .filter(distinctByKey(FilterInfos::getId)) + .collect(Collectors.toMap(FilterInfos::getId, FilterInfos::getName)); + + Map exportFilters = ModificationUtils.getUuidFilterEquipmentsMap(filterService, network, subReportNode, filters, modificationInfos.getErrorType()); + if (exportFilters != null) { + Map exportedFiltersWithWrongEquipmentIds = ModificationUtils.getUuidFilterWrongEquipmentsIdsMap(subReportNode, exportFilters, filters); + List identifiableAttributes = ModificationUtils.getIdentifiableAttributes(exportFilters, exportedFiltersWithWrongEquipmentIds, modificationInfos.getFilters(), subReportNode); + + if (CollectionUtils.isEmpty(identifiableAttributes)) { + String filterNames = modificationInfos.getFilters().stream().map(FilterInfos::getName).collect(Collectors.joining(", ")); + createReport(subReportNode, + "allFiltersWrong", + "All of the following filters have equipments with wrong id : ${filterNames}", + Map.of("filterNames", filterNames), TypedValue.WARN_SEVERITY); + } else { + subReportNode.newReportNode() + .withMessageTemplate("equipmentDeleted", "${nbEquipments} equipments of type=${type} will be removed") + .withUntypedValue("nbEquipments", identifiableAttributes.stream().map(IdentifiableAttributes::getId).count()) + .withUntypedValue("type", modificationInfos.getEquipmentType().name()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + applyFilterDeletion(network, subReportNode, identifiableAttributes); + } + } + } + + private void applyFilterDeletion(Network network, ReportNode subReportNode, List identifiableAttributes) { + IdentifiableType identifiableType = modificationInfos.getEquipmentType(); + if (CONNECTABLE_TYPES.contains(identifiableType)) { + identifiableAttributes.forEach(identifiableAttribute -> new RemoveFeederBay(identifiableAttribute.getId()).apply(network, true, subReportNode)); + } else if (identifiableType == IdentifiableType.VOLTAGE_LEVEL) { + identifiableAttributes.forEach(identifiableAttribute -> new RemoveVoltageLevel(identifiableAttribute.getId()).apply(network, true, subReportNode)); + } else if (identifiableType == IdentifiableType.SUBSTATION) { + identifiableAttributes.forEach(identifiableAttribute -> new RemoveSubstationBuilder().withSubstationId(identifiableAttribute.getId()).build().apply(network, true, subReportNode)); + } else if (identifiableType == IdentifiableType.HVDC_LINE) { + identifiableAttributes.forEach(identifiableAttribute -> removeHvdcLine(network, subReportNode, identifiableAttribute)); + } else { + throw NetworkModificationException.createEquipmentTypeUnknown(identifiableType.name()); + } + } + + private void removeHvdcLine(Network network, ReportNode subReportNode, IdentifiableAttributes identifiableAttribute) { + HvdcLine hvdcLine = (HvdcLine) ModificationUtils.getInstance().getEquipmentByIdentifiableType(network, modificationInfos.getEquipmentType(), identifiableAttribute.getId()); + if (hvdcLine != null) { + HvdcConverterStation converterStation1 = hvdcLine.getConverterStation1(); + HvdcConverterStation converterStation2 = hvdcLine.getConverterStation2(); + if (converterStation1.getHvdcType() == HvdcConverterStation.HvdcType.LCC || converterStation2.getHvdcType() == HvdcConverterStation.HvdcType.LCC) { + String hdvcLineId = identifiableAttribute.getId(); + subReportNode.newReportNode() + .withMessageTemplate("SCNotRemoved" + hdvcLineId, "Shunt compensators were not removed for HVDC line id=${id}") + .withUntypedValue("id", identifiableAttribute.getId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + } + } + new RemoveHvdcLineBuilder().withHvdcLineId(identifiableAttribute.getId()).build().apply(network, true, subReportNode); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/CompositeModification.java b/src/main/java/org/gridsuite/modification/modifications/CompositeModification.java new file mode 100644 index 0000000..c0b763f --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/CompositeModification.java @@ -0,0 +1,25 @@ +/* + Copyright (c) 2024, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.Network; +import org.gridsuite.modification.dto.CompositeModificationInfos; + +/** + * @author Ghazwa Rehili + */ +public class CompositeModification extends AbstractModification { + + public CompositeModification(CompositeModificationInfos compositeModificationInfos) { + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/DeleteAttachingLine.java b/src/main/java/org/gridsuite/modification/modifications/DeleteAttachingLine.java new file mode 100644 index 0000000..171709d --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/DeleteAttachingLine.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.modification.topology.RevertCreateLineOnLine; +import com.powsybl.iidm.modification.topology.RevertCreateLineOnLineBuilder; +import com.powsybl.iidm.network.Network; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.DeleteAttachingLineInfos; + +import static org.gridsuite.modification.NetworkModificationException.Type.LINE_ALREADY_EXISTS; +import static org.gridsuite.modification.NetworkModificationException.Type.LINE_NOT_FOUND; + +/** + * @author bendaamerahm + */ +public class DeleteAttachingLine extends AbstractModification { + + private final DeleteAttachingLineInfos modificationInfos; + + public DeleteAttachingLine(DeleteAttachingLineInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + // check existing lines + if (network.getLine(modificationInfos.getLineToAttachTo1Id()) == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, modificationInfos.getLineToAttachTo1Id()); + } + if (network.getLine(modificationInfos.getLineToAttachTo2Id()) == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, modificationInfos.getLineToAttachTo2Id()); + } + if (network.getLine(modificationInfos.getAttachedLineId()) == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, modificationInfos.getAttachedLineId()); + } + // check future line does not exist + if (network.getLine(modificationInfos.getReplacingLine1Id()) != null) { + throw new NetworkModificationException(LINE_ALREADY_EXISTS, modificationInfos.getReplacingLine1Id()); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + RevertCreateLineOnLineBuilder builder = new RevertCreateLineOnLineBuilder(); + RevertCreateLineOnLine algo = builder.withLineToBeMerged1Id(modificationInfos.getLineToAttachTo1Id()) + .withLineToBeMerged2Id(modificationInfos.getLineToAttachTo2Id()) + .withLineToBeDeletedId(modificationInfos.getAttachedLineId()) + .withMergedLineId(modificationInfos.getReplacingLine1Id()) + .withMergedLineName(modificationInfos.getReplacingLine1Name()) + .build(); + algo.apply(network, true, subReportNode); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/DeleteVoltageLevelOnLine.java b/src/main/java/org/gridsuite/modification/modifications/DeleteVoltageLevelOnLine.java new file mode 100644 index 0000000..a274803 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/DeleteVoltageLevelOnLine.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.modification.topology.RevertConnectVoltageLevelOnLine; +import com.powsybl.iidm.modification.topology.RevertConnectVoltageLevelOnLineBuilder; +import com.powsybl.iidm.network.Network; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.DeleteVoltageLevelOnLineInfos; + +import static org.gridsuite.modification.NetworkModificationException.Type.LINE_ALREADY_EXISTS; +import static org.gridsuite.modification.NetworkModificationException.Type.LINE_NOT_FOUND; + +/** + * @author bendaamerahm + */ +public class DeleteVoltageLevelOnLine extends AbstractModification { + + private final DeleteVoltageLevelOnLineInfos modificationInfos; + + public DeleteVoltageLevelOnLine(DeleteVoltageLevelOnLineInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + // check existing lines + if (network.getLine(modificationInfos.getLineToAttachTo1Id()) == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, modificationInfos.getLineToAttachTo1Id()); + } + if (network.getLine(modificationInfos.getLineToAttachTo2Id()) == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, modificationInfos.getLineToAttachTo2Id()); + } + // check future line does not exist + if (network.getLine(modificationInfos.getReplacingLine1Id()) != null) { + throw new NetworkModificationException(LINE_ALREADY_EXISTS, modificationInfos.getReplacingLine1Id()); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + RevertConnectVoltageLevelOnLineBuilder builder = new RevertConnectVoltageLevelOnLineBuilder(); + RevertConnectVoltageLevelOnLine algo = builder.withLine1Id(modificationInfos.getLineToAttachTo1Id()) + .withLine2Id(modificationInfos.getLineToAttachTo2Id()) + .withLineId(modificationInfos.getReplacingLine1Id()) + .withLineName(modificationInfos.getReplacingLine1Name()) + .build(); + algo.apply(network, true, subReportNode); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/EquipmentAttributeModification.java b/src/main/java/org/gridsuite/modification/modifications/EquipmentAttributeModification.java new file mode 100644 index 0000000..3e767bb --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/EquipmentAttributeModification.java @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.OperatingStatus; +import com.powsybl.iidm.network.extensions.OperatingStatusAdder; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.EquipmentAttributeModificationInfos; + +import static org.gridsuite.modification.NetworkModificationException.Type.EQUIPMENT_NOT_FOUND; +import static org.gridsuite.modification.NetworkModificationException.Type.WRONG_EQUIPMENT_TYPE; + +/** + * @author Slimane Amar + */ +public class EquipmentAttributeModification extends AbstractModification { + + private final EquipmentAttributeModificationInfos modificationInfos; + + public EquipmentAttributeModification(EquipmentAttributeModificationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + Identifiable identifiable = network.getIdentifiable(modificationInfos.getEquipmentId()); + if (identifiable == null) { + throw new NetworkModificationException(EQUIPMENT_NOT_FOUND, modificationInfos.getEquipmentId()); + } + if (identifiable.getType() != modificationInfos.getEquipmentType()) { + throw new NetworkModificationException(WRONG_EQUIPMENT_TYPE, String.format("Type of '%s' is not %s but %s", modificationInfos.getEquipmentId(), modificationInfos.getEquipmentType(), identifiable.getType())); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + Identifiable identifiable = network.getIdentifiable(modificationInfos.getEquipmentId()); + if (identifiable instanceof Switch) { + changeSwitchAttribute((Switch) identifiable, modificationInfos.getEquipmentAttributeName(), modificationInfos.getEquipmentAttributeValue(), subReportNode); + } else if (identifiable instanceof Injection) { + if (identifiable instanceof Generator) { + changeGeneratorAttribute((Generator) identifiable, modificationInfos.getEquipmentAttributeName(), modificationInfos.getEquipmentAttributeValue(), subReportNode); + } + } else if (identifiable instanceof Branch) { + if (identifiable instanceof Line) { + changeLineAttribute((Line) identifiable, modificationInfos.getEquipmentAttributeName(), modificationInfos.getEquipmentAttributeValue(), subReportNode); + } else if (identifiable instanceof TwoWindingsTransformer) { + changeTwoWindingsTransformerAttribute((TwoWindingsTransformer) identifiable, modificationInfos.getEquipmentAttributeName(), modificationInfos.getEquipmentAttributeValue(), subReportNode); + } + } else if (identifiable instanceof ThreeWindingsTransformer) { + changeThreeWindingsTransformerAttribute((ThreeWindingsTransformer) identifiable, modificationInfos.getEquipmentAttributeName(), modificationInfos.getEquipmentAttributeValue(), subReportNode); + } else if (identifiable instanceof HvdcLine) { + // no hvdc line modifications yet + } + } + + private void changeSwitchAttribute(Switch aSwitch, String attributeName, Object attributeValue, ReportNode reportNode) { + if (attributeName.equals("open")) { + if (Boolean.TRUE.equals(aSwitch.isOpen() != (Boolean) attributeValue)) { + aSwitch.setOpen((Boolean) attributeValue); + reportNode.newReportNode() + .withMessageTemplate("switchChanged", "${operation} switch '${id}' in voltage level ${voltageLevelId}") + .withUntypedValue("id", aSwitch.getId()) + .withUntypedValue("operation", Boolean.TRUE.equals(attributeValue) ? "Opening" : "Closing") + .withUntypedValue("voltageLevelId", aSwitch.getVoltageLevel().getId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + } else { + throw NetworkModificationException.createEquipementAttributeNotEditable(aSwitch.getType(), attributeName); + } + } + + // TODO remove only for switch + private void changeGeneratorAttribute(Generator generator, String attributeName, Object attributeValue, ReportNode reportNode) { + if (attributeName.equals("targetP")) { + generator.setTargetP((Double) attributeValue); + reportNode.newReportNode() + .withMessageTemplate("generatorChanged", "Generator with id=${id} targetP changed") + .withUntypedValue("id", generator.getId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } else { + throw NetworkModificationException.createEquipementAttributeNotEditable(generator.getType(), attributeName); + } + } + + // TODO remove only for switch + private void changeLineAttribute(Line line, String attributeName, Object attributeValue, ReportNode reportNode) { + if (attributeName.equals("operatingStatus")) { + line.newExtension(OperatingStatusAdder.class).withStatus(OperatingStatus.Status.valueOf((String) attributeValue)).add(); + reportNode.newReportNode() + .withMessageTemplate("lineStatusChanged", "Branch with id=${id} status changed") + .withUntypedValue("id", line.getId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } else { + throw NetworkModificationException.createEquipementAttributeNotEditable(line.getType(), attributeName); + } + } + + // TODO remove only for switch + private void changeTwoWindingsTransformerAttribute(TwoWindingsTransformer transformer, String attributeName, Object attributeValue, ReportNode reportNode) { + String reportKey; + String reportDefaultMessage; + + switch (attributeName) { + case "ratioTapChanger.tapPosition": + transformer.getOptionalRatioTapChanger().ifPresent(r -> r.setTapPosition((Integer) attributeValue)); + reportKey = "ratioTapPositionChanged"; + reportDefaultMessage = "2WT with id=${id} ratio tap changer position changed"; + break; + case "phaseTapChanger.tapPosition": + reportKey = "phaseTapPositionChanged"; + reportDefaultMessage = "2WT with id=${id} phase tap changer position changed"; + break; + default: + throw NetworkModificationException.createEquipementAttributeNotEditable(transformer.getType(), attributeName); + } + + reportNode.newReportNode() + .withMessageTemplate(reportKey, reportDefaultMessage) + .withUntypedValue("id", transformer.getId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + + // TODO remove only for switch + private void changeThreeWindingsTransformerAttribute(ThreeWindingsTransformer transformer, String attributeName, Object attributeValue, ReportNode reportNode) { + String reportKey; + String reportDefaultMessage; + + switch (attributeName) { + case "ratioTapChanger1.tapPosition": + transformer.getLeg1().getOptionalRatioTapChanger().ifPresent(r -> r.setTapPosition((Integer) attributeValue)); + reportKey = "ratioTapChanger1.tapPosition"; + reportDefaultMessage = "3WT with id=${id} ratio tap changer 1 position changed"; + break; + case "ratioTapChanger2.tapPosition": + transformer.getLeg2().getOptionalRatioTapChanger().ifPresent(r -> r.setTapPosition((Integer) attributeValue)); + reportKey = "ratioTapChanger2.tapPosition"; + reportDefaultMessage = "3WT with id=${id} ratio tap changer 2 position changed"; + break; + case "ratioTapChanger3.tapPosition": + transformer.getLeg3().getOptionalRatioTapChanger().ifPresent(r -> r.setTapPosition((Integer) attributeValue)); + reportKey = "ratioTapChanger3.tapPosition"; + reportDefaultMessage = "3WT with id=${id} ratio tap changer 3 position changed"; + break; + case "phaseTapChanger1.tapPosition": + transformer.getLeg1().getOptionalPhaseTapChanger().ifPresent(p -> p.setTapPosition((Integer) attributeValue)); + reportKey = "phaseTapChanger1.tapPosition"; + reportDefaultMessage = "3WT with id=${id} phase tap changer 1 position changed"; + break; + case "phaseTapChanger2.tapPosition": + transformer.getLeg2().getOptionalPhaseTapChanger().ifPresent(p -> p.setTapPosition((Integer) attributeValue)); + reportKey = "phaseTapChanger2.tapPosition"; + reportDefaultMessage = "3WT with id=${id} phase tap changer 2 position changed"; + break; + case "phaseTapChanger3.tapPosition": + transformer.getLeg3().getOptionalPhaseTapChanger().ifPresent(p -> p.setTapPosition((Integer) attributeValue)); + reportKey = "phaseTapChanger3.tapPosition"; + reportDefaultMessage = "3WT with id=${id} phase tap changer 3 position changed"; + break; + default: + throw NetworkModificationException.createEquipementAttributeNotEditable(transformer.getType(), attributeName); + } + + reportNode.newReportNode() + .withMessageTemplate(reportKey, reportDefaultMessage) + .withUntypedValue("id", transformer.getId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/EquipmentDeletion.java b/src/main/java/org/gridsuite/modification/modifications/EquipmentDeletion.java new file mode 100644 index 0000000..23bc50f --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/EquipmentDeletion.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.modification.topology.*; +import com.powsybl.iidm.network.*; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.EquipmentDeletionInfos; +import org.gridsuite.modification.dto.HvdcLccDeletionInfos; +import org.gridsuite.modification.utils.ModificationUtils; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.gridsuite.modification.NetworkModificationException.Type.EQUIPMENT_NOT_FOUND; + +/** + * @author Ayoub Labidi + */ +public class EquipmentDeletion extends AbstractModification { + + private final EquipmentDeletionInfos modificationInfos; + + public EquipmentDeletion(EquipmentDeletionInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + Identifiable identifiable = ModificationUtils.getInstance().getEquipmentByIdentifiableType(network, modificationInfos.getEquipmentType(), modificationInfos.getEquipmentId()); + if (identifiable == null) { + throw new NetworkModificationException(EQUIPMENT_NOT_FOUND, "Equipment with id=" + modificationInfos.getEquipmentId() + " not found or of bad type"); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + Identifiable identifiable = ModificationUtils.getInstance().getEquipmentByIdentifiableType(network, modificationInfos.getEquipmentType(), modificationInfos.getEquipmentId()); + if (identifiable instanceof Connectable) { + new RemoveFeederBay(modificationInfos.getEquipmentId()).apply(network, true, subReportNode); + } else if (identifiable instanceof HvdcLine) { + removeHvdcLine(network, subReportNode); + } else if (identifiable instanceof VoltageLevel) { + new RemoveVoltageLevel(modificationInfos.getEquipmentId()).apply(network, true, subReportNode); + } else if (identifiable instanceof Substation) { + RemoveSubstation rs = new RemoveSubstationBuilder().withSubstationId(modificationInfos.getEquipmentId()).build(); + rs.apply(network, true, subReportNode); + } + subReportNode.newReportNode() + .withMessageTemplate("equipmentDeleted", "equipment of type=${type} and id=${id} deleted") + .withUntypedValue("type", modificationInfos.getEquipmentType().name()) + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + + private void removeHvdcLine(Network network, ReportNode subReportNode) { + HvdcLccDeletionInfos specificInfos = (HvdcLccDeletionInfos) modificationInfos.getEquipmentInfos(); + List shuntCompensatorIds = List.of(); + if (specificInfos != null) { + shuntCompensatorIds = Stream.concat( + specificInfos.getMcsOnSide1() != null ? specificInfos.getMcsOnSide1().stream() : Stream.of(), + specificInfos.getMcsOnSide2() != null ? specificInfos.getMcsOnSide2().stream() : Stream.of()) + .filter(mcsInfo -> { + // isConnectedToHvdc means: selected to be removed (can be changed by the Front) + if (mcsInfo.isConnectedToHvdc() && network.getShuntCompensator(mcsInfo.getId()) == null) { + subReportNode.newReportNode() + .withMessageTemplate("shuntCompensatorNotDeleted", "Shunt compensator with id=${id} not found in the network") + .withUntypedValue("id", mcsInfo.getId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + return false; + } else { + return mcsInfo.isConnectedToHvdc(); + } + }) + .map(HvdcLccDeletionInfos.ShuntCompensatorInfos::getId) + .collect(Collectors.toList()); + } + RemoveHvdcLine algo = new RemoveHvdcLineBuilder() + .withHvdcLineId(modificationInfos.getEquipmentId()) + .withShuntCompensatorIds(shuntCompensatorIds) + .build(); + algo.apply(network, true, subReportNode); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/GenerationDispatch.java b/src/main/java/org/gridsuite/modification/modifications/GenerationDispatch.java new file mode 100644 index 0000000..137c286 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/GenerationDispatch.java @@ -0,0 +1,651 @@ +/* + Copyright (c) 2023, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.google.common.util.concurrent.AtomicDouble; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.ReportNodeAdder; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.modification.scalable.Scalable; +import com.powsybl.iidm.modification.scalable.ScalingParameters; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.GeneratorStartup; +import lombok.Builder; +import lombok.Getter; + +import org.gridsuite.modification.IFilterService; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.*; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.Comparator.comparingInt; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toCollection; +import static org.gridsuite.modification.NetworkModificationException.Type.GENERATION_DISPATCH_ERROR; + +/** + * @author Franck Lecuyer + */ +public class GenerationDispatch extends AbstractModification { + private static final String SYNCHRONOUS_COMPONENT = "SC"; + private static final String POWER_TO_DISPATCH = "PowerToDispatch"; + private static final String STACKING = "Stacking"; + private static final String RESULT = "Result"; + private static final String GENERATOR = "generator"; + private static final String SUBSTATION = "substation"; + private static final String REGION_CVG = "regionCvg"; + private static final String IS_PLURAL = "isPlural"; + private static final double EPSILON = 0.001; + private static final String GENERATORS_WITH_FIXED_SUPPLY = "generatorsWithFixedSupply"; + private static final String GENERATORS_WITHOUT_OUTAGE = "generatorsWithoutOutage"; + private static final String GENERATORS_FREQUENCY_RESERVE = "generatorsFrequencyReserve"; + + private final GenerationDispatchInfos generationDispatchInfos; + + protected IFilterService filterService; + + public GenerationDispatch(GenerationDispatchInfos generationDispatchInfos) { + this.generationDispatchInfos = generationDispatchInfos; + } + + private static void report(ReportNode reportNode, String suffixKey, String key, String defaultMessage, Map values, TypedValue severity) { + ReportNodeAdder adder = reportNode.newReportNode() + .withMessageTemplate(key + suffixKey, defaultMessage) + .withSeverity(severity); + for (Map.Entry valueEntry : values.entrySet()) { + adder.withUntypedValue(valueEntry.getKey(), valueEntry.getValue().toString()); + } + adder.add(); + } + + private static double computeTotalActiveLoad(Component component) { + Objects.requireNonNull(component); + return component.getBusStream().flatMap(Bus::getLoadStream) + .filter(load -> load.getTerminal().isConnected()) + .mapToDouble(Load::getP0) + .sum(); + } + + private static double computeTotalDemand(Component component, double lossCoefficient) { + double totalLoad = computeTotalActiveLoad(component); + return totalLoad * (1. + lossCoefficient / 100.); + } + + private static double computeTotalActiveBatteryTargetP(Component component) { + Objects.requireNonNull(component); + return component.getBusStream().flatMap(Bus::getBatteryStream) + .filter(battery -> battery.getTerminal().isConnected()) + .mapToDouble(Battery::getTargetP) + .sum(); + } + + private static double computeTotalAmountFixedSupply(Network network, Component component, List generatorsWithFixedSupply, ReportNode reportNode) { + double totalAmountFixedSupply = 0.; + List generatorsWithoutSetpointList = new ArrayList<>(); + totalAmountFixedSupply += generatorsWithFixedSupply.stream().map(network::getGenerator) + .filter(generator -> generator != null && generator.getTerminal().isConnected() && + generator.getTerminal().getBusView().getBus().getSynchronousComponent().getNum() == component.getNum()) + .peek(generator -> { + GeneratorStartup startupExtension = generator.getExtension(GeneratorStartup.class); + if (startupExtension != null && !Double.isNaN(startupExtension.getPlannedActivePowerSetpoint())) { + generator.setTargetP(startupExtension.getPlannedActivePowerSetpoint()); + } else { + generator.setTargetP(0.); + generatorsWithoutSetpointList.add(generator); + + } + }) + .mapToDouble(Generator::getTargetP).sum(); + if (!generatorsWithoutSetpointList.isEmpty()) { + report(reportNode, Integer.toString(component.getNum()), "GeneratorsWithoutPredefinedActivePowerSetpoint", + "${numGeneratorsWithoutSetpoint} generator${isPlural} not have a predefined active power set point", + Map.of("numGeneratorsWithoutSetpoint", generatorsWithoutSetpointList.size(), + IS_PLURAL, generatorsWithoutSetpointList.size() > 1 ? "s do" : " does"), TypedValue.WARN_SEVERITY); + } + + // Report details for each generator without a predefined setpoint + generatorsWithoutSetpointList.forEach(generator -> + report(reportNode, Integer.toString(component.getNum()), "MissingPredefinedActivePowerSetpointForGenerator", + "The generator ${generatorId} does not have a predefined active power set point", + Map.of("generatorId", generator.getId()), TypedValue.TRACE_SEVERITY)); + return totalAmountFixedSupply; + } + + private static boolean inDifferentSynchronousComponent(HvdcConverterStation station, int componentNum) { + Bus bus = station.getTerminal().getBusView().getBus(); + return bus != null && bus.getSynchronousComponent().getNum() != componentNum; + } + + private static double computeHvdcBalance(Component component) { + AtomicDouble balance = new AtomicDouble(0.); + + component.getBusStream().forEach(bus -> { + double hdvcFlow = Stream.concat(bus.getLccConverterStationStream(), bus.getVscConverterStationStream()) + .filter(station -> { + // Keep only hvdc linking to another synchronous component + HvdcLine hvdcLine = station.getHvdcLine(); + HvdcConverterStation station1 = hvdcLine.getConverterStation1(); + HvdcConverterStation station2 = hvdcLine.getConverterStation2(); + + boolean station2NotInComponent = station1.getId().equals(station.getId()) && inDifferentSynchronousComponent(station2, component.getNum()); + boolean station1NotInComponent = station2.getId().equals(station.getId()) && inDifferentSynchronousComponent(station1, component.getNum()); + return station1NotInComponent || station2NotInComponent; + }) + .mapToDouble(station -> { + // compute hvdc flux : import or export + HvdcLine hvdcLine = station.getHvdcLine(); + HvdcConverterStation station1 = hvdcLine.getConverterStation1(); + HvdcConverterStation station2 = hvdcLine.getConverterStation2(); + + if (station1.getId().equals(station.getId()) && + hvdcLine.getConvertersMode() == HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER || + station2.getId().equals(station.getId()) && + hvdcLine.getConvertersMode() == HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER) { + return -hvdcLine.getActivePowerSetpoint(); + } else { + return hvdcLine.getActivePowerSetpoint(); + } + }).sum(); + balance.addAndGet(hdvcFlow); + }); + return balance.get(); + } + + private static Double getGeneratorMarginalCost(Generator generator) { + GeneratorStartup startupExtension = generator.getExtension(GeneratorStartup.class); + if (startupExtension != null && !Double.isNaN(startupExtension.getMarginalCost())) { + return startupExtension.getMarginalCost(); + } + return null; + } + + private static Map> getGeneratorsByMarginalCost(List generators, ReportNode reportNode, String reporterSuffixKey) { + Map> generatorsByMarginalCost = new TreeMap<>(); + + // set targetP to 0 + generators.forEach(generator -> generator.setTargetP(0.)); + + // get generators with marginal cost + List generatorsWithMarginalCost = generators.stream() + .filter(generator -> getGeneratorMarginalCost(generator) != null) + .collect(Collectors.toList()); + int nbNoCost = generators.size() - generatorsWithMarginalCost.size(); + if (nbNoCost > 0) { + report(reportNode, reporterSuffixKey, "NbGeneratorsWithNoCost", "${nbNoCost} generator${isPlural} been discarded from generation dispatch because of missing marginal cost. Their active power set point has been set to 0", + Map.of("nbNoCost", nbNoCost, + IS_PLURAL, nbNoCost > 1 ? "s have" : " has"), + TypedValue.INFO_SEVERITY); + } + generators.stream() + .filter(generator -> getGeneratorMarginalCost(generator) == null) + .forEach(g -> report(reportNode, reporterSuffixKey, "MissingMarginalCostForGenerator", "The generator ${generator} does not have a marginal cost", + Map.of(GENERATOR, g.getId()), TypedValue.TRACE_SEVERITY) + ); + + // build map of generators by marginal cost + generatorsWithMarginalCost.sort(Comparator.comparing(GenerationDispatch::getGeneratorMarginalCost)); + generatorsWithMarginalCost.forEach(g -> { + Double marginalCost = getGeneratorMarginalCost(g); + generatorsByMarginalCost.computeIfAbsent(marginalCost, k -> new ArrayList<>()); + generatorsByMarginalCost.get(marginalCost).add(g.getId()); + }); + + return generatorsByMarginalCost; + } + + private static void reportUnknownSubstations(Network network, List substationsGeneratorsOrderingInfos, ReportNode reportNode, String reporterSuffixKey) { + if (!CollectionUtils.isEmpty(substationsGeneratorsOrderingInfos)) { + substationsGeneratorsOrderingInfos.forEach(sInfo -> + sInfo.getSubstationIds().forEach(sId -> { + Substation substation = network.getSubstation(sId); + if (substation == null) { + report(reportNode, reporterSuffixKey, "SubstationNotFound", "Substation ${substation} not found", + Map.of(SUBSTATION, sId), TypedValue.WARN_SEVERITY); + } + })); + } + } + + private static List computeAdjustableGenerators(Network network, Component component, List generatorsWithFixedSupply, + List substationsGeneratorsOrderingInfos, + ReportNode reportNode) { + List generatorsToReturn = new ArrayList<>(); + String reporterSuffixKey = Integer.toString(component.getNum()); + + // log substations not found + reportUnknownSubstations(network, substationsGeneratorsOrderingInfos, reportNode, reporterSuffixKey); + + // get all connected generators in the component + List generators = component.getBusStream().flatMap(Bus::getGeneratorStream).collect(Collectors.toList()); + + // remove generators with fixed supply + generators.removeIf(generator -> generatorsWithFixedSupply.contains(generator.getId())); + + Map> generatorsByMarginalCost = getGeneratorsByMarginalCost(generators, reportNode, reporterSuffixKey); + generatorsByMarginalCost.forEach((mCost, gList) -> { // loop on generators of same cost + if (!CollectionUtils.isEmpty(substationsGeneratorsOrderingInfos)) { // substations hierarchy provided + // build mapGeneratorsBySubstationsList, that will contain all the generators with the same marginal cost as mCost contained in each list of substations + LinkedHashMap> mapGeneratorsBySubstationsList = new LinkedHashMap<>(); + + AtomicInteger i = new AtomicInteger(0); + substationsGeneratorsOrderingInfos.forEach(sInfo -> { + mapGeneratorsBySubstationsList.computeIfAbsent(i.get(), k -> new TreeSet<>()); + + // get generators with marginal cost == mCost in all substations of the current list + sInfo.getSubstationIds().forEach(sId -> { + Substation substation = network.getSubstation(sId); + if (substation != null) { + substation.getVoltageLevelStream().forEach(v -> + v.getGeneratorStream().filter(g -> { + Double generatorCost = getGeneratorMarginalCost(g); + return generatorCost != null && generatorCost.equals(mCost); + }).forEach(g -> mapGeneratorsBySubstationsList.get(i.get()).add(g.getId()))); + } + }); + + i.incrementAndGet(); + }); + + // loop until all the generators have been encountered + AtomicBoolean finished = new AtomicBoolean(false); + while (!finished.get()) { + finished.set(true); + mapGeneratorsBySubstationsList.values().forEach(generatorsSet -> { + if (generatorsSet.isEmpty()) { // no generators + return; + } + Optional gId = generatorsSet.stream().findFirst(); + generatorsToReturn.add(gId.get()); + generatorsSet.remove(gId.get()); + finished.set(false); + }); + } + + // add in the result the generators with same cost not found in mapGeneratorsBySubstationsList sorted in alphabetical order + gList.stream().sorted().forEach(gId -> { + if (!generatorsToReturn.contains(gId)) { + generatorsToReturn.add(gId); + } + }); + } else { // no substations hierarchy provided + // add in the result the generators in gList sorted in alphabetical order + gList.stream().sorted().forEach(generatorsToReturn::add); + } + }); + + if (generatorsToReturn.isEmpty()) { + report(reportNode, reporterSuffixKey, "NoAvailableAdjustableGenerator", "There is no adjustable generator", + Map.of(), TypedValue.WARN_SEVERITY); + } + + return generatorsToReturn.stream().map(network::getGenerator).toList(); + } + + private static class GeneratorTargetPListener extends DefaultNetworkListener { + private final ReportNode reportNode; + private final String suffixKey; + private final List updatedGenerators = new ArrayList<>(); + + GeneratorTargetPListener(ReportNode reportNode, String suffixKey) { + this.reportNode = reportNode; + this.suffixKey = suffixKey; + } + + @Override + public void onUpdate(Identifiable identifiable, String attribute, String variantId, Object oldValue, Object newValue) { + if (identifiable.getType() == IdentifiableType.GENERATOR && attribute.equals("targetP") && Double.compare((double) oldValue, (double) newValue) != 0) { + updatedGenerators.add((Generator) identifiable); + } + } + + public void endReport(List adjustableGenerators) { + // report updated generators + report(reportNode, suffixKey, "TotalGeneratorSetTargetP", "The active power set points of ${nbUpdatedGenerator} generator${isPlural} have been updated as a result of generation dispatch", + Map.of("nbUpdatedGenerator", updatedGenerators.size(), IS_PLURAL, updatedGenerators.size() > 1 ? "s" : ""), TypedValue.INFO_SEVERITY); + updatedGenerators.forEach(g -> report(reportNode, suffixKey, "GeneratorSetTargetP", "The active power set point of generator ${generator} has been set to ${newValue} MW", + Map.of(GENERATOR, g.getId(), "newValue", round(g.getTargetP())), TypedValue.TRACE_SEVERITY)); + + // report unchanged generators + int nbUnchangedGenerators = adjustableGenerators.size() - updatedGenerators.size(); + if (nbUnchangedGenerators > 0) { + List updatedGeneratorsIds = updatedGenerators.stream().map(Identifiable::getId).toList(); + report(reportNode, suffixKey, "TotalGeneratorUnchangedTargetP", "${nbUnchangedGenerator} eligible generator${isPlural} not been selected by the merit order algorithm. Their active power set point has been set to 0", + Map.of("nbUnchangedGenerator", nbUnchangedGenerators, + IS_PLURAL, nbUnchangedGenerators > 1 ? "s have" : " has"), TypedValue.INFO_SEVERITY); + adjustableGenerators.stream() + .filter(g -> !updatedGeneratorsIds.contains(g.getId())) + .forEach(g -> report(reportNode, suffixKey, "GeneratorUnchangedTargetP", "Generator ${generator} has not been selected by the merit order algorithm. Its active power set point has been set to 0", + Map.of(GENERATOR, g.getId()), TypedValue.TRACE_SEVERITY)); + } + // report the max marginal cost used + Double maxUsedMarginalCost = updatedGenerators.stream() + .map(GenerationDispatch::getGeneratorMarginalCost) + .filter(Objects::nonNull) + .mapToDouble(Double::doubleValue).max().orElseThrow(); + + report(reportNode, suffixKey, "MaxUsedMarginalCost", "Marginal cost: ${maxUsedMarginalCost}", + Map.of("maxUsedMarginalCost", maxUsedMarginalCost), TypedValue.INFO_SEVERITY); + } + } + + @Builder + @Getter + private static final class GeneratorsFrequencyReserve { + private final List generators; + private final double frequencyReserve; + } + + @Override + public void initApplicationContext(IFilterService filterService) { + this.filterService = filterService; + } + + @Override + public void check(Network network) throws NetworkModificationException { + double lossCoefficient = generationDispatchInfos.getLossCoefficient(); + if (lossCoefficient < 0. || lossCoefficient > 100.) { + throw new NetworkModificationException(GENERATION_DISPATCH_ERROR, "The loss coefficient must be between 0 and 100"); + } + double defaultOutageRate = generationDispatchInfos.getDefaultOutageRate(); + if (defaultOutageRate < 0. || defaultOutageRate > 100.) { + throw new NetworkModificationException(GENERATION_DISPATCH_ERROR, "The default outage rate must be between 0 and 100"); + } + } + + private List exportFilters(List generatorsFilters, Network network, ReportNode subReportNode, String generatorsType) { + if (CollectionUtils.isEmpty(generatorsFilters)) { + return List.of(); + } + var filters = generatorsFilters.stream().collect(Collectors.toMap(GeneratorsFilterInfos::getId, GeneratorsFilterInfos::getName, (id1, id2) -> id1, LinkedHashMap::new)); + + // export filters + Map exportedGenerators = filterService + .exportFilters(new ArrayList<>(filters.keySet()), network) + .map(f -> new FilterEquipments(f.getFilterId(), filters.get(f.getFilterId()), + f.getIdentifiableAttributes().stream().map(i -> new IdentifiableAttributes(i.getId(), i.getType(), i.getDistributionKey())).toList(), + f.getNotFoundEquipments())) + .collect(Collectors.toMap(FilterEquipments::getFilterId, Function.identity())); + + // report filters with generators not found + Map filtersWithGeneratorsNotFound = exportedGenerators.entrySet().stream() + .filter(e -> !CollectionUtils.isEmpty(e.getValue().getNotFoundEquipments())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + filtersWithGeneratorsNotFound.values().forEach(f -> { + var filterName = filters.get(f.getFilterId()); + var notFoundGenerators = f.getNotFoundEquipments(); + report(subReportNode, generatorsType, "filterGeneratorsNotFound", getGeneratorsReportMessagePrefix(generatorsType) + ": Cannot find ${nbNotFoundGen} generators in filter ${filterName}", + Map.of("nbNotFoundGen", notFoundGenerators.size(), "filterName", filterName), + TypedValue.WARN_SEVERITY); + f.getNotFoundEquipments().forEach(e -> report(subReportNode, generatorsType, "generatorNotFound" + e, getGeneratorsReportMessagePrefix(generatorsType) + ": Cannot find generator ${notFoundGeneratorId} in filter ${filterName}", + Map.of("notFoundGeneratorId", e, "filterName", filterName), TypedValue.TRACE_SEVERITY)); + }); + + // return existing generators + return exportedGenerators.values().stream() + .flatMap(f -> f.getIdentifiableAttributes().stream()) + .map(IdentifiableAttributes::getId) + .distinct() + .collect(Collectors.toList()); + } + + private List collectGeneratorsWithoutOutage(Network network, ReportNode subReportNode) { + return exportFilters(generationDispatchInfos.getGeneratorsWithoutOutage(), network, subReportNode, GENERATORS_WITHOUT_OUTAGE); + } + + private List collectGeneratorsWithFixedSupply(Network network, ReportNode subReportNode) { + return exportFilters(generationDispatchInfos.getGeneratorsWithFixedSupply(), network, subReportNode, GENERATORS_WITH_FIXED_SUPPLY); + } + + private List collectGeneratorsWithFrequencyReserve(Network network, ReportNode subReportNode) { + return generationDispatchInfos.getGeneratorsFrequencyReserve().stream().map(g -> { + List generators = exportFilters(g.getGeneratorsFilters(), network, subReportNode, GENERATORS_FREQUENCY_RESERVE); + return GeneratorsFrequencyReserve.builder().generators(generators).frequencyReserve(g.getFrequencyReserve()).build(); + }).collect(Collectors.toList()); + } + + private static double computeGenFrequencyReserve(Generator generator, + List generatorsFrequencyReserve) { + AtomicReference freqReserve = new AtomicReference<>(0.); + generatorsFrequencyReserve.forEach(g -> { + if (g.getGenerators().contains(generator.getId())) { + freqReserve.set(g.getFrequencyReserve()); + } + }); + return freqReserve.get(); + } + + private double reduceGeneratorMaxPValue(Generator generator, + List generatorsWithoutOutage, + List generatorsFrequencyReserve) { + double res = generator.getMaxP(); + if (!generatorsWithoutOutage.contains(generator.getId())) { + GeneratorStartup startupExtension = generator.getExtension(GeneratorStartup.class); + if (startupExtension != null && + !Double.isNaN(startupExtension.getForcedOutageRate()) && + !Double.isNaN(startupExtension.getPlannedOutageRate())) { + res *= (1. - startupExtension.getForcedOutageRate()) * (1. - startupExtension.getPlannedOutageRate()); + } else { + res *= 1. - generationDispatchInfos.getDefaultOutageRate() / 100.; + } + } + double genFrequencyReserve = computeGenFrequencyReserve(generator, generatorsFrequencyReserve); + return Math.max(generator.getMinP(), res * (1. - genFrequencyReserve / 100.)); + } + + private void reportDisconnectedGenerators(List globalDisconnectedGenerators, int componentNum, ReportNode reportNode) { + List componentDisconnectedGenerators = globalDisconnectedGenerators.stream() + .filter(g -> g.getTerminal().getBusView() != null && g.getTerminal().getBusView().getConnectableBus() != null && + g.getTerminal().getBusView().getConnectableBus().getSynchronousComponent().getNum() == componentNum) + .toList(); + if (!componentDisconnectedGenerators.isEmpty()) { + report(reportNode, Integer.toString(componentNum), "TotalDisconnectedGenerator", "${nbDisconnectedGenerator} generator${isPlural} been discarded from generation dispatch because their are disconnected. Their active power set point remains unchanged", + Map.of("nbDisconnectedGenerator", componentDisconnectedGenerators.size(), + IS_PLURAL, componentDisconnectedGenerators.size() > 1 ? "s have" : " has"), + TypedValue.INFO_SEVERITY); + componentDisconnectedGenerators.forEach(g -> + report(reportNode, Integer.toString(componentNum), "DisconnectedGenerator", "Generator ${generator} has been discarded from generation dispatch because it is disconnected. Its active power set point remains unchanged", + Map.of(GENERATOR, g.getId()), TypedValue.TRACE_SEVERITY) + ); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + Collection synchronousComponents = network.getBusView().getBusStream() + .filter(Bus::isInMainConnectedComponent) + .map(Bus::getSynchronousComponent) + .collect(collectingAndThen(toCollection(() -> new TreeSet<>(comparingInt(Component::getNum))), ArrayList::new)); + + report(subReportNode, "", "NbSynchronousComponents", "Network has ${scNumber} synchronous component${isPlural}: ${scList}", + Map.of("scNumber", synchronousComponents.size(), + IS_PLURAL, synchronousComponents.size() > 1 ? "s" : "", + "scList", synchronousComponents.stream().map(sc -> "SC" + sc.getNum()).collect(Collectors.joining(", "))), + TypedValue.INFO_SEVERITY); + + // all disconnected generators at network level (for report purpose) + List disconnectedGenerators = network.getGeneratorStream() + .filter(g -> !g.getTerminal().isConnected()) + .toList(); + + // get generators for which there will be no reduction of maximal power + List generatorsWithoutOutage = collectGeneratorsWithoutOutage(network, subReportNode); + + // get generators with fixed supply + List generatorsWithFixedSupply = collectGeneratorsWithFixedSupply(network, subReportNode); + + // get generators with frequency reserve + List generatorsWithFrequencyReserve = collectGeneratorsWithFrequencyReserve(network, subReportNode); + + for (Component component : synchronousComponents) { + int componentNum = component.getNum(); + + ReportNode componentReportNode = subReportNode.newReportNode() + .withMessageTemplate("Network CC0 " + SYNCHRONOUS_COMPONENT + componentNum, "Network CC0 " + SYNCHRONOUS_COMPONENT + componentNum) + .add(); + + ReportNode powerToDispatchReportNode = componentReportNode.newReportNode() + .withMessageTemplate(POWER_TO_DISPATCH, POWER_TO_DISPATCH) + .add(); + + // log disconnected generators attached to this synchronous component + reportDisconnectedGenerators(disconnectedGenerators, componentNum, powerToDispatchReportNode); + + // get total value of connected loads in the connected component + double totalDemand = computeTotalDemand(component, generationDispatchInfos.getLossCoefficient()); + report(powerToDispatchReportNode, Integer.toString(componentNum), "TotalDemand", "The total demand is : ${totalDemand} MW", + Map.of("totalDemand", round(totalDemand)), TypedValue.INFO_SEVERITY); + + // get total supply value for generators with fixed supply + double totalAmountFixedSupply = computeTotalAmountFixedSupply(network, component, generatorsWithFixedSupply, powerToDispatchReportNode); + report(powerToDispatchReportNode, Integer.toString(componentNum), "TotalAmountFixedSupply", "The total amount of fixed supply is : ${totalAmountFixedSupply} MW", + Map.of("totalAmountFixedSupply", round(totalAmountFixedSupply)), TypedValue.INFO_SEVERITY); + + // compute hvdc balance to other synchronous components + double hvdcBalance = computeHvdcBalance(component); + report(powerToDispatchReportNode, Integer.toString(componentNum), "TotalOutwardHvdcFlow", "The HVDC balance is : ${hvdcBalance} MW", + Map.of("hvdcBalance", round(hvdcBalance)), TypedValue.INFO_SEVERITY); + + double activeBatteryTotalTargetP = computeTotalActiveBatteryTargetP(component); + report(powerToDispatchReportNode, Integer.toString(componentNum), "TotalActiveBatteryTargetP", "The battery balance is : ${batteryBalance} MW", + Map.of("batteryBalance", round(activeBatteryTotalTargetP)), TypedValue.INFO_SEVERITY); + + double totalAmountSupplyToBeDispatched = totalDemand - totalAmountFixedSupply - hvdcBalance - activeBatteryTotalTargetP; + if (totalAmountSupplyToBeDispatched < 0.) { + report(powerToDispatchReportNode, Integer.toString(componentNum), "TotalAmountFixedSupplyExceedsTotalDemand", "The total amount of fixed supply exceeds the total demand", + Map.of(), TypedValue.WARN_SEVERITY); + continue; + } else { + report(powerToDispatchReportNode, Integer.toString(componentNum), "TotalAmountSupplyToBeDispatched", "The total amount of supply to be dispatched is : ${totalAmountSupplyToBeDispatched} MW", + Map.of("totalAmountSupplyToBeDispatched", round(totalAmountSupplyToBeDispatched)), TypedValue.INFO_SEVERITY); + } + + // get adjustable generators in the component + List adjustableGenerators = computeAdjustableGenerators(network, component, generatorsWithFixedSupply, + generationDispatchInfos.getSubstationsGeneratorsOrdering(), + powerToDispatchReportNode); + + double realized = 0.; + if (!adjustableGenerators.isEmpty()) { + // stacking of adjustable generators to ensure the totalAmountSupplyToBeDispatched + List generatorsScalable = adjustableGenerators.stream().map(generator -> { + double minValue = generator.getMinP(); + double maxValue = reduceGeneratorMaxPValue(generator, generatorsWithoutOutage, generatorsWithFrequencyReserve); + return (Scalable) Scalable.onGenerator(generator.getId(), minValue, maxValue); + }).toList(); + + ReportNode stackingReportNode = componentReportNode.newReportNode() + .withMessageTemplate(STACKING, STACKING) + .add(); + + GeneratorTargetPListener listener = new GeneratorTargetPListener(stackingReportNode, Integer.toString(componentNum)); + network.addListener(listener); + + Scalable scalable = Scalable.stack(generatorsScalable.toArray(Scalable[]::new)); + realized = scalable.scale(network, totalAmountSupplyToBeDispatched, new ScalingParameters().setAllowsGeneratorOutOfActivePowerLimits(true)); + + listener.endReport(adjustableGenerators); + network.removeListener(listener); + } + + ReportNode resultReporter = componentReportNode.newReportNode() + .withMessageTemplate(RESULT, RESULT) + .add(); + + if (Math.abs(totalAmountSupplyToBeDispatched - realized) < EPSILON) { + Map> generatorsByRegion = getGeneratorsByRegion(network, component); + + report(resultReporter, Integer.toString(componentNum), "SupplyDemandBalanceCouldBeMet", "The supply-demand balance could be met", + Map.of(), TypedValue.INFO_SEVERITY); + generatorsByRegion.forEach((region, generators) -> { + Map activePowerSumByEnergySource = getActivePowerSumByEnergySource(generators); + report(resultReporter, Integer.toString(componentNum), "SumGeneratorActivePower" + region, "Sum of generator active power setpoints in ${region} region: ${sum} MW (NUCLEAR: ${nuclearSum} MW, THERMAL: ${thermalSum} MW, HYDRO: ${hydroSum} MW, WIND AND SOLAR: ${windAndSolarSum} MW, OTHER: ${otherSum} MW).", + Map.of("region", region, + "sum", round(activePowerSumByEnergySource.values().stream().reduce(0d, Double::sum)), + "nuclearSum", round(activePowerSumByEnergySource.getOrDefault(EnergySource.NUCLEAR, 0d)), + "thermalSum", round(activePowerSumByEnergySource.getOrDefault(EnergySource.THERMAL, 0d)), + "hydroSum", round(activePowerSumByEnergySource.getOrDefault(EnergySource.HYDRO, 0d)), + "windAndSolarSum", round(activePowerSumByEnergySource.getOrDefault(EnergySource.WIND, 0d) + activePowerSumByEnergySource.getOrDefault(EnergySource.SOLAR, 0d)), + "otherSum", round(activePowerSumByEnergySource.getOrDefault(EnergySource.OTHER, 0d)) + ), TypedValue.INFO_SEVERITY); + }); + } else { + double remainingPowerImbalance = totalAmountSupplyToBeDispatched - realized; + report(resultReporter, Integer.toString(componentNum), "SupplyDemandBalanceCouldNotBeMet", "The supply-demand balance could not be met : the remaining power imbalance is ${remainingPower} MW", + Map.of("remainingPower", round(remainingPowerImbalance)), TypedValue.WARN_SEVERITY); + } + } + } + + private Map> getGeneratorsByRegion(Network network, Component component) { + // get all connected generators that are inside the synchronous component and the substationIds associated. + List connectedGenerators = network.getGeneratorStream() + .filter(g -> g.getTerminal().isConnected() && g.getTerminal().getBusView().getBus().getSynchronousComponent().getNum() == component.getNum()) + .toList(); + List substationIds = connectedGenerators.stream() + .map(g -> g.getTerminal().getVoltageLevel().getSubstation().map(Substation::getId).orElse(null)) + .filter(Objects::nonNull) + .toList(); + // get all substations with "regionCvg" property name + Map substationIdPropertiesMap = new HashMap<>(); + if (!CollectionUtils.isEmpty(substationIds)) { + substationIds.forEach(sId -> { + Substation substation = network.getSubstation(sId); + if (!substation.getPropertyNames().isEmpty() && hasCvgPropertyName(substation.getPropertyNames())) { + substation.getPropertyNames().forEach(property -> { + if (REGION_CVG.equals(property)) { + substationIdPropertiesMap.put(substation.getId(), substation.getProperty(property)); + } + }); + } + }); + } + + // group substationIds by region + Map> groupedSubstationIds = substationIdPropertiesMap.keySet().stream().collect(Collectors.groupingBy(substationIdPropertiesMap::get)); + + // iterate over groupedSubstationIds and check for each substation list if it's related to the connected generators + Map> generatorsByRegion = new HashMap<>(); + + groupedSubstationIds.forEach((region, substationList) -> { + List connectedGeneratorsWithSubstation = connectedGenerators.stream() + .filter(g -> substationList.contains(g.getTerminal().getVoltageLevel().getSubstation().map(Substation::getId).orElse(null))) + .toList(); + generatorsByRegion.put(region, connectedGeneratorsWithSubstation); + }); + + return generatorsByRegion; + } + + private boolean hasCvgPropertyName(Set propertyNames) { + return propertyNames.stream().anyMatch(REGION_CVG::equals); + } + + private Map getActivePowerSumByEnergySource(List generators) { + return generators.stream().collect(Collectors.toMap(Generator::getEnergySource, Generator::getTargetP, Double::sum)); + } + + private static double round(double value) { + return Math.round(value * 10) / 10.; + } + + private static String getGeneratorsReportMessagePrefix(String generatorsType) { + return switch (generatorsType) { + case GENERATORS_WITH_FIXED_SUPPLY -> "Generators with fixed active power"; + case GENERATORS_WITHOUT_OUTAGE -> "Generators without outage simulation"; + case GENERATORS_FREQUENCY_RESERVE -> "Frequency reserve"; + default -> ""; + }; + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/GeneratorCreation.java b/src/main/java/org/gridsuite/modification/modifications/GeneratorCreation.java new file mode 100644 index 0000000..4734c12 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/GeneratorCreation.java @@ -0,0 +1,336 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; +import com.powsybl.iidm.network.extensions.GeneratorShortCircuitAdder; +import com.powsybl.network.store.iidm.impl.extensions.CoordinatedReactiveControlAdderImpl; +import com.powsybl.network.store.iidm.impl.extensions.GeneratorStartupAdderImpl; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.GeneratorCreationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import java.util.ArrayList; +import java.util.List; + +import static org.gridsuite.modification.NetworkModificationException.Type.*; +import static org.gridsuite.modification.modifications.GeneratorModification.ERROR_MESSAGE; +import static org.gridsuite.modification.utils.ModificationUtils.*; + +/** + * @author Ayoub Labidi + */ +public class GeneratorCreation extends AbstractModification { + + private final GeneratorCreationInfos modificationInfos; + private static final String LIMITS = "Limits"; + private static final String ACTIVE_LIMITS = "Active limits"; + + public GeneratorCreation(GeneratorCreationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (network.getGenerator(modificationInfos.getEquipmentId()) != null) { + throw new NetworkModificationException(GENERATOR_ALREADY_EXISTS, modificationInfos.getEquipmentId()); + } + + // check connectivity + ModificationUtils.getInstance().controlConnectivity(network, modificationInfos.getVoltageLevelId(), + modificationInfos.getBusOrBusbarSectionId(), modificationInfos.getConnectionPosition()); + + // check reactive limits + ModificationUtils.getInstance().checkReactiveLimitsCreation(modificationInfos, + modificationInfos.getErrorType(), + modificationInfos.getEquipmentId(), + "Generator"); + + // check regulated terminal + VoltageLevel voltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId()); + ModificationUtils.getInstance().getTerminalFromIdentifiable(voltageLevel.getNetwork(), + modificationInfos.getRegulatingTerminalId(), + modificationInfos.getRegulatingTerminalType(), + modificationInfos.getRegulatingTerminalVlId()); + + ModificationUtils.getInstance().checkActivePowerControl(modificationInfos.getParticipate(), + modificationInfos.getDroop(), CREATE_GENERATOR_ERROR, String.format(ERROR_MESSAGE, modificationInfos.getEquipmentId())); + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + // create the generator in the network + VoltageLevel voltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId()); + if (voltageLevel.getTopologyKind() == TopologyKind.NODE_BREAKER) { + createGeneratorInNodeBreaker(voltageLevel, modificationInfos, network, subReportNode); + } else { + createGeneratorInBusBreaker(voltageLevel, modificationInfos, subReportNode); + } + if (!modificationInfos.isTerminalConnected()) { + network.getGenerator(modificationInfos.getEquipmentId()).getTerminal().disconnect(); + } + // apply the properties + Generator generator = network.getGenerator(modificationInfos.getEquipmentId()); + PropertiesUtils.applyProperties(generator, subReportNode, modificationInfos.getProperties(), "GeneratorProperties"); + } + + private void createGeneratorInNodeBreaker(VoltageLevel voltageLevel, GeneratorCreationInfos generatorCreationInfos, Network network, ReportNode subReportNode) { + GeneratorAdder generatorAdder = createGeneratorAdderInNodeBreaker(voltageLevel, generatorCreationInfos); + createInjectionInNodeBreaker(voltageLevel, generatorCreationInfos, network, generatorAdder, subReportNode); + + // CreateFeederBayBuilder already create the generator using + // (withInjectionAdder(generatorAdder)) so then we can add the additional informations and extensions + var generator = ModificationUtils.getInstance().getGenerator(network, generatorCreationInfos.getEquipmentId()); + addExtensionsToGenerator(generatorCreationInfos, generator, voltageLevel, subReportNode); + } + + private GeneratorAdder createGeneratorAdderInNodeBreaker(VoltageLevel voltageLevel, GeneratorCreationInfos generatorCreationInfos) { + Terminal terminal = ModificationUtils.getInstance().getTerminalFromIdentifiable(voltageLevel.getNetwork(), + generatorCreationInfos.getRegulatingTerminalId(), + generatorCreationInfos.getRegulatingTerminalType(), + generatorCreationInfos.getRegulatingTerminalVlId()); + + // creating the generator adder + GeneratorAdder generatorAdder = voltageLevel.newGenerator() + .setId(generatorCreationInfos.getEquipmentId()) + .setName(generatorCreationInfos.getEquipmentName()) + .setEnergySource(generatorCreationInfos.getEnergySource()) + .setMinP(generatorCreationInfos.getMinP()) + .setMaxP(generatorCreationInfos.getMaxP()) + .setRatedS(nanIfNull(generatorCreationInfos.getRatedS())) + .setTargetP(generatorCreationInfos.getTargetP()) + .setTargetQ(nanIfNull(generatorCreationInfos.getTargetQ())) + .setVoltageRegulatorOn(generatorCreationInfos.isVoltageRegulationOn()) + .setTargetV(nanIfNull(generatorCreationInfos.getTargetV())); + + if (terminal != null) { + generatorAdder.setRegulatingTerminal(terminal); + } + + return generatorAdder; + } + + private void addExtensionsToGenerator(GeneratorCreationInfos generatorCreationInfos, Generator generator, + VoltageLevel voltageLevel, ReportNode subReportNode) { + if (generatorCreationInfos.getEquipmentName() != null) { + ModificationUtils.getInstance().reportElementaryCreation(subReportNode, generatorCreationInfos.getEquipmentName(), "Name"); + } + if (generatorCreationInfos.getEnergySource() != null) { + ModificationUtils.getInstance().reportElementaryCreation(subReportNode, generatorCreationInfos.getEnergySource(), "Energy source"); + } + reportInjectionCreationConnectivity(generatorCreationInfos, subReportNode); + ReportNode subReporterLimits = reportGeneratorActiveLimits(generatorCreationInfos, subReportNode); + ModificationUtils.getInstance().createReactiveLimits(generatorCreationInfos, generator, subReporterLimits); + ReportNode subReporterSetpoints = reportGeneratorSetPoints(generatorCreationInfos, subReportNode); + createGeneratorVoltageRegulation(generatorCreationInfos, generator, voltageLevel, subReporterSetpoints); + createGeneratorActivePowerControl(generatorCreationInfos, generator, subReporterSetpoints); + createGeneratorShortCircuit(generatorCreationInfos, generator, subReportNode); + createGeneratorStartUp(generatorCreationInfos, generator, subReportNode); + } + + private void createGeneratorInBusBreaker(VoltageLevel voltageLevel, GeneratorCreationInfos generatorCreationInfos, ReportNode subReportNode) { + Bus bus = ModificationUtils.getInstance().getBusBreakerBus(voltageLevel, generatorCreationInfos.getBusOrBusbarSectionId()); + + // creating the generator + Generator generator = voltageLevel.newGenerator() + .setId(generatorCreationInfos.getEquipmentId()) + .setName(generatorCreationInfos.getEquipmentName()) + .setEnergySource(generatorCreationInfos.getEnergySource()) + .setBus(bus.getId()) + .setConnectableBus(bus.getId()) + .setMinP(generatorCreationInfos.getMinP()) + .setMaxP(generatorCreationInfos.getMaxP()) + .setRatedS(nanIfNull(generatorCreationInfos.getRatedS())) + .setTargetP(generatorCreationInfos.getTargetP()) + .setTargetQ(nanIfNull(generatorCreationInfos.getTargetQ())) + .setVoltageRegulatorOn(generatorCreationInfos.isVoltageRegulationOn()) + .setTargetV(nanIfNull(generatorCreationInfos.getTargetV())) + .add(); + + addExtensionsToGenerator(generatorCreationInfos, generator, voltageLevel, subReportNode); + + subReportNode.newReportNode() + .withMessageTemplate("generatorCreated", "New generator with id=${id} created") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + + private ReportNode reportGeneratorSetPoints(GeneratorCreationInfos generatorCreationInfos, ReportNode subReportNode) { + List setPointReports = new ArrayList<>(); + setPointReports.add(ModificationUtils.getInstance() + .buildCreationReport(generatorCreationInfos.getTargetP(), "Active power")); + if (generatorCreationInfos.getTargetQ() != null) { + setPointReports.add(ModificationUtils.getInstance() + .buildCreationReport(generatorCreationInfos.getTargetQ(), "Reactive power")); + } + return ModificationUtils.getInstance().reportModifications(subReportNode, setPointReports, "SetPointCreated", "Setpoints"); + } + + private void createGeneratorVoltageRegulation(GeneratorCreationInfos generatorCreationInfos, Generator generator, VoltageLevel voltageLevel, ReportNode subReportNode) { + List voltageReports = new ArrayList<>(); + voltageReports.add(ModificationUtils.getInstance() + .createEnabledDisabledReport("VoltageRegulationOn", modificationInfos.isVoltageRegulationOn())); + voltageReports.add(ModificationUtils.getInstance().buildCreationReport(generatorCreationInfos.getTargetV(), "Voltage")); + if (generatorCreationInfos.getRegulatingTerminalVlId() != null && generatorCreationInfos.getRegulatingTerminalId() != null && + generatorCreationInfos.getRegulatingTerminalType() != null) { + Terminal terminal = ModificationUtils.getInstance().getTerminalFromIdentifiable(voltageLevel.getNetwork(), + generatorCreationInfos.getRegulatingTerminalId(), + generatorCreationInfos.getRegulatingTerminalType(), + generatorCreationInfos.getRegulatingTerminalVlId()); + if (terminal != null) { + updateGeneratorRegulatingTerminal(generatorCreationInfos, generator, terminal, voltageReports); + } + } + if (generatorCreationInfos.getQPercent() != null) { + try { + generator.newExtension(CoordinatedReactiveControlAdderImpl.class) + .withQPercent(generatorCreationInfos.getQPercent()).add(); + voltageReports.add(ModificationUtils.getInstance().buildCreationReport(generatorCreationInfos.getQPercent(), "Reactive percentage")); + } catch (PowsyblException e) { + voltageReports.add(ReportNode.newRootReportNode() + .withMessageTemplate("ReactivePercentageError", "cannot add Coordinated reactive extension on generator with id=${id} : ${message}") + .withUntypedValue("id", generatorCreationInfos.getEquipmentId()) + .withUntypedValue("message", e.getMessage()) + .withSeverity(TypedValue.ERROR_SEVERITY) + .build()); + } + } + ModificationUtils.getInstance().reportModifications(subReportNode, voltageReports, "VoltageRegulationCreated", "Voltage regulation"); + + } + + private void updateGeneratorRegulatingTerminal(GeneratorCreationInfos generatorCreationInfos, Generator generator, + Terminal terminal, List voltageReports) { + if (generatorCreationInfos.getRegulatingTerminalId() != null + && generatorCreationInfos.getRegulatingTerminalType() != null + && generatorCreationInfos.getRegulatingTerminalVlId() != null) { + generator.setRegulatingTerminal(terminal); + voltageReports.add(ModificationUtils.getInstance().buildCreationReport( + generatorCreationInfos.getRegulatingTerminalVlId(), + "Voltage level")); + voltageReports.add(ModificationUtils.getInstance().buildCreationReport( + generatorCreationInfos.getRegulatingTerminalType() + ":" + + generatorCreationInfos.getRegulatingTerminalId(), + "Equipment")); + } + } + + private ReportNode reportGeneratorActiveLimits(GeneratorCreationInfos generatorCreationInfos, ReportNode subReportNode) { + ReportNode subReportNodeLimits = subReportNode.newReportNode().withMessageTemplate(LIMITS, LIMITS).add(); + List limitsReports = new ArrayList<>(); + limitsReports.add(ModificationUtils.getInstance().buildCreationReport( + generatorCreationInfos.getMinP(), "Min active power")); + limitsReports.add(ModificationUtils.getInstance().buildCreationReport( + generatorCreationInfos.getMaxP(), "Max active power")); + if (generatorCreationInfos.getRatedS() != null) { + limitsReports.add(ModificationUtils.getInstance().buildCreationReport( + generatorCreationInfos.getRatedS(), "Rated nominal power")); + } + ModificationUtils.getInstance().reportModifications(subReportNodeLimits, limitsReports, "ActiveLimitsCreated", ACTIVE_LIMITS); + return subReportNodeLimits; + } + + private void createGeneratorActivePowerControl(GeneratorCreationInfos generatorCreationInfos, Generator generator, ReportNode subReportNode) { + if (generatorCreationInfos.getParticipate() != null && generatorCreationInfos.getDroop() != null) { + List activePowerRegulationReports = new ArrayList<>(); + try { + generator.newExtension(ActivePowerControlAdder.class) + .withParticipate(generatorCreationInfos.getParticipate()) + .withDroop(generatorCreationInfos.getDroop()) + .add(); + activePowerRegulationReports.add(ModificationUtils.getInstance().buildCreationReport( + generatorCreationInfos.getParticipate(), + "Participate")); + activePowerRegulationReports.add(ModificationUtils.getInstance().buildCreationReport( + generatorCreationInfos.getDroop(), + "Droop")); + } catch (PowsyblException e) { + activePowerRegulationReports.add(ReportNode.newRootReportNode() + .withMessageTemplate("ActivePowerExtensionAddError", "cannot add active power extension on generator with id=${id} : ${message}") + .withUntypedValue("id", generatorCreationInfos.getEquipmentId()) + .withUntypedValue("message", e.getMessage()) + .withSeverity(TypedValue.ERROR_SEVERITY) + .build()); + + } + ModificationUtils.getInstance().reportModifications(subReportNode, activePowerRegulationReports, "ActivePowerRegulationCreated", "Active power regulation"); + } + } + + private void createGeneratorShortCircuit(GeneratorCreationInfos generatorCreationInfos, Generator generator, ReportNode subReportNode) { + if (generatorCreationInfos.getDirectTransX() != null) { + List shortCircuitReports = new ArrayList<>(); + try { + GeneratorShortCircuitAdder shortCircuitAdder = generator.newExtension(GeneratorShortCircuitAdder.class) + .withDirectTransX(generatorCreationInfos.getDirectTransX()); + if (generatorCreationInfos.getStepUpTransformerX() != null) { + shortCircuitAdder.withStepUpTransformerX(generatorCreationInfos.getStepUpTransformerX()); + } + shortCircuitAdder.add(); + shortCircuitReports.add(ModificationUtils.getInstance().buildCreationReport(generatorCreationInfos.getDirectTransX(), "Transient reactance")); + if (generatorCreationInfos.getStepUpTransformerX() != null) { + shortCircuitReports.add(ModificationUtils.getInstance().buildCreationReport(generatorCreationInfos.getStepUpTransformerX(), "Transformer reactance")); + } + } catch (PowsyblException e) { + shortCircuitReports.add(ReportNode.newRootReportNode() + .withMessageTemplate("ShortCircuitExtensionAddError", "cannot add short-circuit extension on generator with id=${id} : ${message}") + .withUntypedValue("id", generatorCreationInfos.getEquipmentId()) + .withUntypedValue("message", e.getMessage()) + .withSeverity(TypedValue.ERROR_SEVERITY) + .build()); + } + ModificationUtils.getInstance().reportModifications(subReportNode, shortCircuitReports, "shortCircuitCreated", "Short-circuit"); + } + } + + private void createGeneratorStartUp(GeneratorCreationInfos generatorCreationInfos, Generator generator, ReportNode subReportNode) { + if (generatorCreationInfos.getPlannedActivePowerSetPoint() != null + || generatorCreationInfos.getMarginalCost() != null + || generatorCreationInfos.getPlannedOutageRate() != null + || generatorCreationInfos.getForcedOutageRate() != null) { + List startupReports = new ArrayList<>(); + try { + generator.newExtension(GeneratorStartupAdderImpl.class) + .withPlannedActivePowerSetpoint(nanIfNull(generatorCreationInfos.getPlannedActivePowerSetPoint())) + .withMarginalCost(nanIfNull(generatorCreationInfos.getMarginalCost())) + .withPlannedOutageRate(nanIfNull(generatorCreationInfos.getPlannedOutageRate())) + .withForcedOutageRate(nanIfNull(generatorCreationInfos.getForcedOutageRate())) + .add(); + if (generatorCreationInfos.getPlannedActivePowerSetPoint() != null) { + startupReports.add(ModificationUtils.getInstance().buildCreationReport( + generatorCreationInfos.getPlannedActivePowerSetPoint(), "Planning active power set point")); + } + if (generatorCreationInfos.getMarginalCost() != null) { + startupReports.add(ModificationUtils.getInstance().buildCreationReport( + generatorCreationInfos.getMarginalCost(), "Marginal cost")); + } + if (generatorCreationInfos.getPlannedOutageRate() != null) { + startupReports.add(ModificationUtils.getInstance().buildCreationReport( + generatorCreationInfos.getPlannedOutageRate(), "Planning outage rate")); + } + if (generatorCreationInfos.getForcedOutageRate() != null) { + startupReports.add(ModificationUtils.getInstance().buildCreationReport( + generatorCreationInfos.getForcedOutageRate(), "Forced outage rate")); + } + } catch (PowsyblException e) { + startupReports.add(ReportNode.newRootReportNode() + .withMessageTemplate("StartupExtensionAddError", "cannot add startup extension on generator with id=${id} : ${message}") + .withUntypedValue("id", generatorCreationInfos.getEquipmentId()) + .withMessageTemplate("message", e.getMessage()) + .withSeverity(TypedValue.ERROR_SEVERITY) + .build()); + } + ModificationUtils.getInstance().reportModifications(subReportNode, startupReports, "startUpAttributesCreated", "Start up"); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java new file mode 100644 index 0000000..f38bf35 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java @@ -0,0 +1,493 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.*; +import com.powsybl.network.store.iidm.impl.extensions.CoordinatedReactiveControlAdderImpl; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.*; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.gridsuite.modification.NetworkModificationException.Type.MODIFY_GENERATOR_ERROR; +import static org.gridsuite.modification.utils.ModificationUtils.insertReportNode; + +/** + * @author Ayoub Labidi + */ +public class GeneratorModification extends AbstractModification { + + private static final String LIMITS = "Limits"; + private static final String ACTIVE_LIMITS = "Active limits"; + private static final String SETPOINTS = "Setpoints"; + public static final String ERROR_MESSAGE = "Generator '%s' : "; + + private final GeneratorModificationInfos modificationInfos; + + public GeneratorModification(GeneratorModificationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (modificationInfos == null) { + throw new NetworkModificationException(MODIFY_GENERATOR_ERROR, "Missing required attributes to modify the equipment"); + } + Generator generator = ModificationUtils.getInstance().getGenerator(network, modificationInfos.getEquipmentId()); + // check min max reactive limits + String errorMessage = "Generator '" + modificationInfos.getEquipmentId() + "' : "; + ModificationUtils.getInstance().checkReactiveLimit(generator, modificationInfos.getMinQ(), modificationInfos.getMaxQ(), + modificationInfos.getReactiveCapabilityCurvePoints(), MODIFY_GENERATOR_ERROR, errorMessage); + // check regulated terminal + if (modificationInfos.getRegulatingTerminalId() != null && modificationInfos.getRegulatingTerminalType() != null && + modificationInfos.getRegulatingTerminalVlId() != null) { + VoltageLevel voltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getRegulatingTerminalVlId().getValue()); + ModificationUtils.getInstance().getTerminalFromIdentifiable(voltageLevel.getNetwork(), + modificationInfos.getRegulatingTerminalId().getValue(), + modificationInfos.getRegulatingTerminalType().getValue(), + modificationInfos.getRegulatingTerminalVlId().getValue()); + } + checkActivePowerZeroOrBetweenMinAndMaxActivePowerGenerator(modificationInfos, generator, MODIFY_GENERATOR_ERROR, errorMessage); + } + + private void checkActivePowerZeroOrBetweenMinAndMaxActivePowerGenerator(GeneratorModificationInfos modificationInfos, Generator generator, NetworkModificationException.Type exceptionType, String errorMessage) { + ModificationUtils.getInstance().checkActivePowerZeroOrBetweenMinAndMaxActivePower( + modificationInfos.getTargetP(), + modificationInfos.getMinP(), + modificationInfos.getMaxP(), + generator.getMinP(), + generator.getMaxP(), + generator.getTargetP(), + exceptionType, + errorMessage + ); + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + Generator generator = ModificationUtils.getInstance().getGenerator(network, modificationInfos.getEquipmentId()); + // modify the generator in the network + modifyGenerator(generator, modificationInfos, subReportNode); + } + + private void modifyGenerator(Generator generator, GeneratorModificationInfos modificationInfos, ReportNode subReportNode) { + subReportNode.newReportNode() + .withMessageTemplate("generatorModification", "Generator with id=${id} modified :") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + + if (modificationInfos.getEquipmentName() != null && modificationInfos.getEquipmentName().getValue() != null) { + ModificationUtils.getInstance().applyElementaryModifications(generator::setName, () -> generator.getOptionalName().orElse("No value"), modificationInfos.getEquipmentName(), subReportNode, "Name"); + } + ModificationUtils.getInstance().applyElementaryModifications(generator::setEnergySource, generator::getEnergySource, modificationInfos.getEnergySource(), subReportNode, "Energy source"); + + modifyGeneratorLimitsAttributes(modificationInfos, generator, subReportNode); + modifyGeneratorSetpointsAttributes(modificationInfos, generator, subReportNode); + modifyGeneratorShortCircuitAttributes(modificationInfos.getDirectTransX(), modificationInfos.getStepUpTransformerX(), generator, subReportNode); + modifyGeneratorStartUpAttributes(modificationInfos, generator, subReportNode); + modifyGeneratorConnectivityAttributes(modificationInfos, generator, subReportNode); + PropertiesUtils.applyProperties(generator, subReportNode, modificationInfos.getProperties(), "GeneratorProperties"); + } + + public static void modifyGeneratorShortCircuitAttributes(AttributeModification directTransX, + AttributeModification stepUpTransformerX, + Generator generator, + ReportNode subReportNode) { + List reports = new ArrayList<>(); + GeneratorShortCircuit generatorShortCircuit = generator.getExtension(GeneratorShortCircuit.class); + Double oldTransientReactance = generatorShortCircuit != null ? generatorShortCircuit.getDirectTransX() : Double.NaN; + Double oldStepUpTransformerReactance = generatorShortCircuit != null ? generatorShortCircuit.getStepUpTransformerX() : Double.NaN; + // Either transient reactance or step-up transformer reactance are modified or + // both + if (directTransX != null && stepUpTransformerX != null) { + generator.newExtension(GeneratorShortCircuitAdder.class) + .withDirectTransX(directTransX.getValue()) + .withStepUpTransformerX(stepUpTransformerX.getValue()) + .add(); + reports.add(ModificationUtils.getInstance().buildModificationReport( + oldTransientReactance, + directTransX.getValue(), + "Transient reactance")); + reports.add(ModificationUtils.getInstance().buildModificationReport( + oldStepUpTransformerReactance, + stepUpTransformerX.getValue(), + "Transformer reactance")); + + } else if (directTransX != null) { + generator.newExtension(GeneratorShortCircuitAdder.class) + .withStepUpTransformerX(oldStepUpTransformerReactance) + .withDirectTransX(directTransX.getValue()) + .add(); + reports.add(ModificationUtils.getInstance().buildModificationReport( + oldTransientReactance, + directTransX.getValue(), + "Transient reactance")); + } else if (stepUpTransformerX != null) { + generator.newExtension(GeneratorShortCircuitAdder.class) + .withStepUpTransformerX(stepUpTransformerX.getValue()) + .withDirectTransX(oldTransientReactance) + .add(); + reports.add(ModificationUtils.getInstance().buildModificationReport( + oldStepUpTransformerReactance, + stepUpTransformerX.getValue(), + "Transformer reactance")); + } + if (subReportNode != null) { + ModificationUtils.getInstance().reportModifications(subReportNode, reports, "shortCircuitAttributesModified", "Short-circuit"); + } + } + + private void modifyGeneratorReactiveCapabilityCurvePoints(GeneratorModificationInfos modificationInfos, + Generator generator, ReportNode subReportNode, ReportNode subReportNodeLimits) { + ReactiveCapabilityCurveAdder adder = generator.newReactiveCapabilityCurve(); + List modificationPoints = modificationInfos.getReactiveCapabilityCurvePoints(); + Collection points = generator.getReactiveLimits().getKind() == ReactiveLimitsKind.CURVE ? generator.getReactiveLimits(ReactiveCapabilityCurve.class).getPoints() : List.of(); + ModificationUtils.getInstance().modifyReactiveCapabilityCurvePoints(points, modificationPoints, adder, subReportNode, subReportNodeLimits); + } + + public static ReportNode modifyGeneratorActiveLimitsAttributes(AttributeModification maxP, + AttributeModification minP, + AttributeModification ratedS, + Generator generator, + ReportNode subReportNode) { + ReportNode subReporterLimits = null; + ReportNode reportMaxActivePower; + ReportNode reportMinActivePower; + + if (maxP != null && maxP.getValue() > generator.getMinP()) { + reportMaxActivePower = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(generator::setMaxP, generator::getMaxP, maxP, "Max active power"); + reportMinActivePower = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(generator::setMinP, generator::getMinP, minP, "Min active power"); + + } else { + reportMinActivePower = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(generator::setMinP, generator::getMinP, minP, "Min active power"); + reportMaxActivePower = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(generator::setMaxP, generator::getMaxP, maxP, "Max active power"); + } + ReportNode reportRatedNominalPower = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(generator::setRatedS, generator::getRatedS, ratedS, "Rated nominal power"); + if (subReportNode != null && (reportMaxActivePower != null || reportMinActivePower != null || reportRatedNominalPower != null)) { + subReporterLimits = subReportNode.newReportNode().withMessageTemplate(LIMITS, LIMITS).add(); + ReportNode subReporterActiveLimits = subReporterLimits.newReportNode().withMessageTemplate(ACTIVE_LIMITS, ACTIVE_LIMITS).add(); + if (reportMaxActivePower != null) { + insertReportNode(subReporterActiveLimits, reportMaxActivePower); + } + if (reportMinActivePower != null) { + insertReportNode(subReporterActiveLimits, reportMinActivePower); + } + if (reportRatedNominalPower != null) { + insertReportNode(subReporterActiveLimits, reportRatedNominalPower); + } + } + return subReporterLimits; + } + + private void modifyGeneratorReactiveLimitsAttributes(GeneratorModificationInfos modificationInfos, + Generator generator, ReportNode subReportNode, ReportNode subReportNodeLimits) { + // if reactive capability curve is true and there was modifications on the + // reactive capability curve points, + // then we have to apply the reactive capability curve modifications + // else if reactive capability curve is false we have to apply the min and max + // reactive limits modifications + if (modificationInfos.getReactiveCapabilityCurve() != null) { + if (Boolean.TRUE.equals(modificationInfos.getReactiveCapabilityCurve().getValue() + && modificationInfos.getReactiveCapabilityCurvePoints() != null + && !modificationInfos.getReactiveCapabilityCurvePoints().isEmpty())) { + modifyGeneratorReactiveCapabilityCurvePoints(modificationInfos, generator, subReportNode, subReportNodeLimits); + } else if (Boolean.FALSE.equals(modificationInfos.getReactiveCapabilityCurve().getValue())) { + ModificationUtils.getInstance().modifyMinMaxReactiveLimits(modificationInfos.getMinQ(), modificationInfos.getMaxQ(), generator, subReportNode, subReportNodeLimits); + } + } + } + + private ReportNode modifyGeneratorActivePowerControlAttributes(GeneratorModificationInfos modificationInfos, + Generator generator, ReportNode subReportNode, ReportNode subReportNodeSetpoints) { + ActivePowerControl activePowerControl = generator.getExtension(ActivePowerControl.class); + ActivePowerControlAdder activePowerControlAdder = generator.newExtension(ActivePowerControlAdder.class); + + return ModificationUtils.getInstance().modifyActivePowerControlAttributes(activePowerControl, activePowerControlAdder, + modificationInfos.getParticipate(), modificationInfos.getDroop(), subReportNode, subReportNodeSetpoints, + MODIFY_GENERATOR_ERROR, String.format(ERROR_MESSAGE, modificationInfos.getEquipmentId())); + } + + private void modifyGeneratorStartUpAttributes(GeneratorModificationInfos modificationInfos, Generator generator, + ReportNode subReportNode) { + List reports = new ArrayList<>(); + modifyGeneratorStartUpAttributes(modificationInfos.getPlannedActivePowerSetPoint(), + modificationInfos.getMarginalCost(), + modificationInfos.getPlannedOutageRate(), + modificationInfos.getForcedOutageRate(), + generator, + subReportNode, + reports); + } + + public static void modifyGeneratorStartUpAttributes(AttributeModification plannedActivePowerSetPoint, + AttributeModification marginalCost, + AttributeModification plannedOutageRate, + AttributeModification forcedOutageRate, + Generator generator, + ReportNode subReportNode, + List reports) { + GeneratorStartupAdder generatorStartupAdder = generator.newExtension(GeneratorStartupAdder.class); + GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); + boolean plannedActivePowerSetPointUpdated = addPlannedActivePowerSetPoint(plannedActivePowerSetPoint, + generatorStartupAdder, + generatorStartup, + reports); + boolean marginalCostUpdated = addMarginalCost(marginalCost, generatorStartupAdder, generatorStartup, reports); + boolean plannedOutageRateUpdated = addPlannedOutageRate(plannedOutageRate, generatorStartupAdder, generatorStartup, reports); + boolean forcedOutageRateUpdated = addForcedOutageRate(forcedOutageRate, generatorStartupAdder, generatorStartup, reports); + + if (plannedActivePowerSetPointUpdated || + marginalCostUpdated || + plannedOutageRateUpdated || + forcedOutageRateUpdated) { + generatorStartupAdder.add(); + if (subReportNode != null) { + ModificationUtils.getInstance().reportModifications(subReportNode, reports, "startUpAttributesModified", "Start up"); + } + } + } + + private static boolean addForcedOutageRate(AttributeModification forcedOutageRate, GeneratorStartupAdder generatorStartupAdder, GeneratorStartup generatorStartup, List reports) { + Double oldForcedOutageRate = generatorStartup != null ? generatorStartup.getForcedOutageRate() : Double.NaN; + if (forcedOutageRate != null) { + generatorStartupAdder + .withForcedOutageRate(forcedOutageRate.getValue()); + if (reports != null) { + reports.add(ModificationUtils.getInstance().buildModificationReport(oldForcedOutageRate, + forcedOutageRate.getValue(), + "Forced outage rate")); + } + return true; + } else { + generatorStartupAdder + .withForcedOutageRate(oldForcedOutageRate); + } + return false; + } + + private static boolean addPlannedOutageRate(AttributeModification plannedOutageRate, GeneratorStartupAdder generatorStartupAdder, GeneratorStartup generatorStartup, List reports) { + Double oldPlannedOutageRate = generatorStartup != null ? generatorStartup.getPlannedOutageRate() : Double.NaN; + if (plannedOutageRate != null) { + generatorStartupAdder + .withPlannedOutageRate(plannedOutageRate.getValue()); + if (reports != null) { + reports.add(ModificationUtils.getInstance().buildModificationReport(oldPlannedOutageRate, + plannedOutageRate.getValue(), + "Planning outage rate")); + } + return true; + } else { + generatorStartupAdder + .withPlannedOutageRate(oldPlannedOutageRate); + } + return false; + } + + private static boolean addMarginalCost(AttributeModification marginalCost, GeneratorStartupAdder generatorStartupAdder, GeneratorStartup generatorStartup, List reports) { + Double oldMarginalCost = generatorStartup != null ? generatorStartup.getMarginalCost() : Double.NaN; + if (marginalCost != null) { + generatorStartupAdder + .withMarginalCost(marginalCost.getValue()); + if (reports != null) { + reports.add(ModificationUtils.getInstance().buildModificationReport(oldMarginalCost, + marginalCost.getValue(), + "Marginal cost")); + } + return true; + } else { + generatorStartupAdder + .withMarginalCost(oldMarginalCost); + } + return false; + } + + private static boolean addPlannedActivePowerSetPoint(AttributeModification plannedActivePowerSetPoint, GeneratorStartupAdder generatorStartupAdder, + GeneratorStartup generatorStartup, List reports) { + Double oldPlannedActivePowerSetPoint = generatorStartup != null ? generatorStartup.getPlannedActivePowerSetpoint() : Double.NaN; + if (plannedActivePowerSetPoint != null) { + generatorStartupAdder + .withPlannedActivePowerSetpoint(plannedActivePowerSetPoint.getValue()); + if (reports != null) { + reports.add(ModificationUtils.getInstance().buildModificationReport(oldPlannedActivePowerSetPoint, + plannedActivePowerSetPoint.getValue(), + "Planning active power set point")); + } + return true; + } else { + generatorStartupAdder + .withPlannedActivePowerSetpoint(oldPlannedActivePowerSetPoint); + } + return false; + } + + private void modifyGeneratorRegulatingTerminal(GeneratorModificationInfos modificationInfos, Generator generator, List modificationReports) { + Terminal regulatingTerminal = generator.getRegulatingTerminal(); + + String oldVoltageLevel = null; + String oldEquipment = null; + // If there is no regulating terminal in file, regulating terminal voltage level + // is equal to generator voltage level + if (regulatingTerminal != null + && !regulatingTerminal.getVoltageLevel().equals(generator.getTerminal().getVoltageLevel())) { + oldVoltageLevel = regulatingTerminal.getVoltageLevel().getId(); + oldEquipment = regulatingTerminal.getConnectable().getType().name() + ":" + + regulatingTerminal.getConnectable().getId(); + } + + if (modificationInfos.getRegulatingTerminalId() != null + && modificationInfos.getRegulatingTerminalType() != null + && modificationInfos.getRegulatingTerminalVlId() != null) { + Terminal terminal = ModificationUtils.getInstance().getTerminalFromIdentifiable(generator.getNetwork(), + modificationInfos.getRegulatingTerminalId().getValue(), + modificationInfos.getRegulatingTerminalType().getValue(), + modificationInfos.getRegulatingTerminalVlId().getValue()); + generator.setRegulatingTerminal(terminal); + + modificationReports.add(ModificationUtils.getInstance().buildModificationReport(oldVoltageLevel, + modificationInfos.getRegulatingTerminalVlId().getValue(), + "Voltage level")); + modificationReports.add(ModificationUtils.getInstance().buildModificationReport(oldEquipment, + modificationInfos.getRegulatingTerminalType().getValue() + ":" + + modificationInfos.getRegulatingTerminalId().getValue(), + "Equipment")); + } + + // if the voltageRegulationType is set to LOCAL, we set the regulatingTerminal + // to null + if (modificationInfos.getVoltageRegulationType() != null + && modificationInfos.getVoltageRegulationType().getValue() == VoltageRegulationType.LOCAL + && oldEquipment != null && oldVoltageLevel != null) { + generator.setRegulatingTerminal(null); + modificationReports.add(ModificationUtils.getInstance().buildModificationReport(oldVoltageLevel, + null, + "Voltage level")); + modificationReports.add(ModificationUtils.getInstance().buildModificationReport(oldEquipment, + null, + "Equipment")); + } + } + + private ReportNode modifyGeneratorVoltageRegulatorAttributes(GeneratorModificationInfos modificationInfos, + Generator generator, ReportNode subReportNode, ReportNode subReportNodeSetpoints) { + List voltageRegulationReports = new ArrayList<>(); + + ReportNode reportVoltageSetpoint = modifyTargetV(generator, modificationInfos.getTargetV()); + + ReportNode voltageRegulationOn = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(generator::setVoltageRegulatorOn, generator::isVoltageRegulatorOn, + modificationInfos.getVoltageRegulationOn(), "VoltageRegulationOn"); + if (voltageRegulationOn != null) { + voltageRegulationReports.add(voltageRegulationOn); + } + if (reportVoltageSetpoint != null) { + voltageRegulationReports.add(reportVoltageSetpoint); + } + + // We apply modifications to regulatingTerminal and QPercent + // we apply modifications to the reactivepower setpoint + modifyGeneratorRegulatingTerminal(modificationInfos, generator, voltageRegulationReports); + if (modificationInfos.getQPercent() != null) { + CoordinatedReactiveControl coordinatedReactiveControl = generator + .getExtension(CoordinatedReactiveControl.class); + Double oldQPercent = coordinatedReactiveControl != null ? coordinatedReactiveControl.getQPercent() + : Double.NaN; + generator.newExtension(CoordinatedReactiveControlAdderImpl.class) + .withQPercent(modificationInfos.getQPercent().getValue()) + .add(); + voltageRegulationReports.add(ModificationUtils.getInstance().buildModificationReport( + oldQPercent, + modificationInfos.getQPercent().getValue(), "Reactive percentage")); + } + + //TargetQ and TargetV are unset after voltage regulation have been dealt with otherwise it can cause unwanted validations exceptions + if (modificationInfos.getTargetV() != null && modificationInfos.getTargetV().getOp() == OperationType.UNSET) { + generator.setTargetV(Double.NaN); + } + + if (modificationInfos.getTargetQ() != null && modificationInfos.getTargetQ().getOp() == OperationType.UNSET) { + generator.setTargetQ(Double.NaN); + } + + ReportNode subReportNodeSetpoints2 = subReportNodeSetpoints; + if (subReportNodeSetpoints == null && !voltageRegulationReports.isEmpty()) { + subReportNodeSetpoints2 = subReportNode.newReportNode() + .withMessageTemplate(SETPOINTS, SETPOINTS) + .add(); + } + ModificationUtils.getInstance().reportModifications(subReportNodeSetpoints2, voltageRegulationReports, "voltageRegulationModified", "Voltage regulation"); + return subReportNodeSetpoints2; + } + + public static ReportNode modifyTargetV(Generator generator, AttributeModification modifTargetV) { + ReportNode reportVoltageSetpoint = null; + if (modifTargetV != null) { + if (modifTargetV.getOp() == OperationType.SET) { + reportVoltageSetpoint = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(generator::setTargetV, generator::getTargetV, + modifTargetV, "Voltage"); + } else { + reportVoltageSetpoint = ModificationUtils.getInstance().buildModificationReport(generator.getTargetV(), Double.NaN, "Voltage"); + } + } + return reportVoltageSetpoint; + } + + private void modifyGeneratorSetpointsAttributes(GeneratorModificationInfos modificationInfos, + Generator generator, ReportNode subReportNode) { + ReportNode reportActivePower = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(generator::setTargetP, generator::getTargetP, modificationInfos.getTargetP(), "Active power"); + + ReportNode reportReactivePower = modifyTargetQ(generator, modificationInfos.getTargetQ()); + + ReportNode subReporterSetpoints = null; + if (reportActivePower != null || reportReactivePower != null) { + subReporterSetpoints = subReportNode.newReportNode().withMessageTemplate(SETPOINTS, SETPOINTS).add(); + if (reportActivePower != null) { + insertReportNode(subReporterSetpoints, reportActivePower); + } + if (reportReactivePower != null) { + insertReportNode(subReporterSetpoints, reportReactivePower); + } + } + subReporterSetpoints = modifyGeneratorVoltageRegulatorAttributes(modificationInfos, generator, subReportNode, subReporterSetpoints); + modifyGeneratorActivePowerControlAttributes(modificationInfos, generator, subReportNode, subReporterSetpoints); + } + + public static ReportNode modifyTargetQ(Generator generator, AttributeModification modifTargetQ) { + ReportNode reportReactivePower = null; + if (modifTargetQ != null) { + if (modifTargetQ.getOp() == OperationType.SET) { + reportReactivePower = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport(generator::setTargetQ, generator::getTargetQ, modifTargetQ, "Target reactive power"); + } else { + reportReactivePower = ModificationUtils.getInstance().buildModificationReport(generator.getTargetQ(), Double.NaN, "Target reactive power"); + } + } + return reportReactivePower; + } + + private void modifyGeneratorLimitsAttributes(GeneratorModificationInfos modificationInfos, + Generator generator, ReportNode subReportNode) { + ReportNode subReportNodeLimits = modifyGeneratorActiveLimitsAttributes(modificationInfos.getMaxP(), + modificationInfos.getMinP(), + modificationInfos.getRatedS(), + generator, + subReportNode); + modifyGeneratorReactiveLimitsAttributes(modificationInfos, generator, subReportNode, subReportNodeLimits); + } + + private ReportNode modifyGeneratorConnectivityAttributes(GeneratorModificationInfos modificationInfos, + Generator generator, ReportNode subReportNode) { + ConnectablePosition connectablePosition = generator.getExtension(ConnectablePosition.class); + ConnectablePositionAdder connectablePositionAdder = generator.newExtension(ConnectablePositionAdder.class); + return ModificationUtils.getInstance().modifyInjectionConnectivityAttributes(connectablePosition, connectablePositionAdder, generator, modificationInfos, subReportNode); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/GeneratorScaling.java b/src/main/java/org/gridsuite/modification/modifications/GeneratorScaling.java new file mode 100644 index 0000000..7df54dc --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/GeneratorScaling.java @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.modification.scalable.Scalable; +import com.powsybl.iidm.modification.scalable.ScalingParameters; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; +import org.gridsuite.modification.VariationType; +import org.gridsuite.modification.dto.GeneratorScalingInfos; +import org.gridsuite.modification.dto.IdentifiableAttributes; +import org.gridsuite.modification.dto.ScalingVariationInfos; +import org.gridsuite.modification.utils.ModificationUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import static com.powsybl.iidm.modification.scalable.ScalingParameters.Priority.RESPECT_OF_VOLUME_ASKED; + +/** + * @author Seddik Yengui + */ + +public class GeneratorScaling extends AbstractScaling { + + public GeneratorScaling(GeneratorScalingInfos generatorScalableInfos) { + super(generatorScalableInfos); + } + + @Override + protected void applyStackingUpVariation(Network network, + ReportNode subReportNode, + List identifiableAttributes, + ScalingVariationInfos generatorScalingVariation) { + AtomicReference sum = new AtomicReference<>(0D); + Scalable stackingUpScalable = Scalable.stack(identifiableAttributes.stream() + .map(attribute -> network.getGenerator(attribute.getId())) + .filter(ModificationUtils::isInjectionConnected) + .map(g -> { + sum.set(g.getTargetP() + sum.get()); + return getScalable(g.getId()); + }).toArray(Scalable[]::new)); + scale(network, subReportNode, generatorScalingVariation, sum, stackingUpScalable, new ScalingParameters()); + } + + @Override + protected void applyVentilationVariation(Network network, + ReportNode subReportNode, + List identifiableAttributes, + ScalingVariationInfos generatorScalingVariation, + Double distributionKeys) { + if (distributionKeys != null) { + AtomicReference sum = new AtomicReference<>(0D); + List percentages = new ArrayList<>(); + List scalables = new ArrayList<>(); + + identifiableAttributes.forEach(equipment -> { + Generator generator = network.getGenerator(equipment.getId()); + if (ModificationUtils.isInjectionConnected(generator)) { + sum.set(generator.getTargetP() + sum.get()); + scalables.add(getScalable(equipment.getId())); + percentages.add((equipment.getDistributionKey() / distributionKeys) * 100); + } + }); + Scalable ventilationScalable = Scalable.proportional(percentages, scalables); + scale(network, subReportNode, generatorScalingVariation, sum, ventilationScalable, new ScalingParameters().setPriority(RESPECT_OF_VOLUME_ASKED)); + } + } + + @Override + protected void applyRegularDistributionVariation(Network network, + ReportNode subReportNode, + List identifiableAttributes, + ScalingVariationInfos generatorScalingVariation) { + List generators = identifiableAttributes + .stream() + .map(attribute -> network.getGenerator(attribute.getId())) + .filter(ModificationUtils::isInjectionConnected) + .toList(); + + AtomicReference sum = new AtomicReference<>(0D); + + List scalables = generators.stream() + .map(generator -> { + sum.set(sum.get() + generator.getTargetP()); + return getScalable(generator.getId()); + }).collect(Collectors.toList()); + + List percentages = new ArrayList<>(Collections.nCopies(scalables.size(), 100.0 / scalables.size())); + Scalable regularDistributionScalable = Scalable.proportional(percentages, scalables); + scale(network, subReportNode, generatorScalingVariation, sum, regularDistributionScalable, new ScalingParameters().setPriority(RESPECT_OF_VOLUME_ASKED)); + } + + @Override + protected void applyProportionalToPmaxVariation(Network network, + ReportNode subReportNode, + List identifiableAttributes, + ScalingVariationInfos generatorScalingVariation) { + AtomicReference maxPSum = new AtomicReference<>(0D); + AtomicReference targetPSum = new AtomicReference<>(0D); + List generators = identifiableAttributes + .stream() + .map(attribute -> network.getGenerator(attribute.getId())) + .filter(ModificationUtils::isInjectionConnected) + .toList(); + Map maxPMap = new HashMap<>(); + List percentages = new ArrayList<>(); + List scalables = new ArrayList<>(); + + // we retrieve max P and the sum of max P of each generator to calculate the percentage. + // we calculate the sum of target P to calculate variation value if variation type is Target_P + generators.forEach(generator -> { + maxPMap.put(generator.getId(), generator.getMaxP()); + maxPSum.set(maxPSum.get() + generator.getMaxP()); + targetPSum.set(targetPSum.get() + generator.getTargetP()); + }); + + setScalablePercentage(maxPSum, maxPMap, percentages, scalables); + Scalable proportionalToPmaxScalable = Scalable.proportional(percentages, scalables); + scale(network, subReportNode, generatorScalingVariation, targetPSum, proportionalToPmaxScalable, new ScalingParameters().setPriority(RESPECT_OF_VOLUME_ASKED)); + } + + @Override + protected void applyProportionalVariation(Network network, + ReportNode subReportNode, + List identifiableAttributes, + ScalingVariationInfos generatorScalingVariation) { + AtomicReference sum = new AtomicReference<>(0D); + List generators = identifiableAttributes + .stream() + .map(attribute -> network.getGenerator(attribute.getId())) + .filter(ModificationUtils::isInjectionConnected) + .toList(); + List percentages = new ArrayList<>(); + Map targetPMap = new HashMap<>(); + List scalables = new ArrayList<>(); + + // we retrieve the target P for every generator and calculate their sum + generators.forEach(generator -> { + targetPMap.put(generator.getId(), generator.getTargetP()); + sum.set(sum.get() + generator.getTargetP()); + }); + + // we calculate percentage of each target P value relative to the sum of target P + setScalablePercentage(sum, targetPMap, percentages, scalables); + Scalable proportionalScalable = Scalable.proportional(percentages, scalables); + scale(network, subReportNode, generatorScalingVariation, sum, proportionalScalable, new ScalingParameters().setPriority(RESPECT_OF_VOLUME_ASKED)); + } + + private void setScalablePercentage(AtomicReference sum, + Map targetPMap, + List percentages, + List scalables) { + targetPMap.forEach((id, p) -> { + percentages.add((p / sum.get()) * 100); + scalables.add(getScalable(id)); + }); + } + + private void scale(Network network, ReportNode subReportNode, ScalingVariationInfos scalingVariationInfos, AtomicReference sum, Scalable scalable, ScalingParameters scalingParameters) { + double asked = getAsked(scalingVariationInfos, sum); + double done = scalable.scale(network, asked, scalingParameters); + subReportNode.newReportNode() + .withMessageTemplate("scalingApplied", "Successfully scaling variation in ${variationMode} mode with variation value asked is ${askedValue} and variation done is ${actualValue}") + .withUntypedValue("variationMode", scalingVariationInfos.getVariationMode().name()) + .withUntypedValue("askedValue", asked) + .withUntypedValue("actualValue", done) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + + @Override + protected double getAsked(ScalingVariationInfos generatorScalingVariation, AtomicReference sum) { + return scalingInfos.getVariationType() == VariationType.DELTA_P + ? generatorScalingVariation.getVariationValue() + : generatorScalingVariation.getVariationValue() - sum.get(); + } + + @Override + protected Scalable getScalable(String id) { + return Scalable.onGenerator(id); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/GroovyScript.java b/src/main/java/org/gridsuite/modification/modifications/GroovyScript.java new file mode 100644 index 0000000..2a25347 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/GroovyScript.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; + +import groovy.lang.Binding; +import groovy.lang.GroovyShell; + +import org.apache.commons.lang3.StringUtils; +import org.codehaus.groovy.control.CompilerConfiguration; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.GroovyScriptInfos; +import static org.gridsuite.modification.NetworkModificationException.Type.GROOVY_SCRIPT_EMPTY; + +/** + * @author Ayoub Labidi + */ +public class GroovyScript extends AbstractModification { + + private final GroovyScriptInfos modificationInfos; + + public GroovyScript(GroovyScriptInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (StringUtils.isBlank(modificationInfos.getScript())) { + throw new NetworkModificationException(GROOVY_SCRIPT_EMPTY); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + var conf = new CompilerConfiguration(); + var binding = new Binding(); + binding.setProperty("network", network); + var shell = new GroovyShell(binding, conf); + shell.evaluate(modificationInfos.getScript()); + + subReportNode.newReportNode() + .withMessageTemplate("groovyScriptApplied", "Groovy script applied") + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/LineAttachToVoltageLevel.java b/src/main/java/org/gridsuite/modification/modifications/LineAttachToVoltageLevel.java new file mode 100644 index 0000000..aacbc98 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/LineAttachToVoltageLevel.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.modification.topology.CreateLineOnLine; +import com.powsybl.iidm.modification.topology.CreateLineOnLineBuilder; +import com.powsybl.iidm.network.*; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.LineAttachToVoltageLevelInfos; +import org.gridsuite.modification.dto.LineCreationInfos; +import org.gridsuite.modification.dto.VoltageLevelCreationInfos; +import org.gridsuite.modification.utils.ModificationUtils; + +import static org.gridsuite.modification.NetworkModificationException.Type.*; +import static org.gridsuite.modification.NetworkModificationException.Type.LINE_ALREADY_EXISTS; + +/** + * @author David Braquart + */ +public class LineAttachToVoltageLevel extends AbstractModification { + + private final LineAttachToVoltageLevelInfos modificationInfos; + + public LineAttachToVoltageLevel(LineAttachToVoltageLevelInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (network.getLine(modificationInfos.getLineToAttachToId()) == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, modificationInfos.getLineToAttachToId()); + } + LineCreationInfos attachmentLineInfos = modificationInfos.getAttachmentLine(); + ModificationUtils.getInstance().controlNewOrExistingVoltageLevel(modificationInfos.getMayNewVoltageLevelInfos(), + modificationInfos.getExistingVoltageLevelId(), modificationInfos.getBbsOrBusId(), network); + // new fictitious VL + if (network.getVoltageLevel(modificationInfos.getAttachmentPointId()) != null) { + throw new NetworkModificationException(VOLTAGE_LEVEL_ALREADY_EXISTS, modificationInfos.getAttachmentPointId()); + } + // check future lines don't exist + if (network.getLine(attachmentLineInfos.getEquipmentId()) != null) { + throw new NetworkModificationException(LINE_ALREADY_EXISTS, attachmentLineInfos.getEquipmentId()); + } + if (network.getLine(modificationInfos.getNewLine1Id()) != null) { + throw new NetworkModificationException(LINE_ALREADY_EXISTS, modificationInfos.getNewLine1Id()); + } + if (network.getLine(modificationInfos.getNewLine2Id()) != null) { + throw new NetworkModificationException(LINE_ALREADY_EXISTS, modificationInfos.getNewLine2Id()); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + VoltageLevelCreationInfos mayNewVL = modificationInfos.getMayNewVoltageLevelInfos(); + if (mayNewVL != null) { + ModificationUtils.getInstance().createVoltageLevel(mayNewVL, subReportNode, network); + } + + LineCreationInfos attachmentLineInfos = modificationInfos.getAttachmentLine(); + LineAdder lineAdder = network.newLine() + .setId(attachmentLineInfos.getEquipmentId()) + .setName(attachmentLineInfos.getEquipmentName()) + .setR(attachmentLineInfos.getR()) + .setX(attachmentLineInfos.getX()) + .setG1(ModificationUtils.getInstance().zeroIfNull(attachmentLineInfos.getG1())) + .setB1(ModificationUtils.getInstance().zeroIfNull(attachmentLineInfos.getB1())) + .setG2(ModificationUtils.getInstance().zeroIfNull(attachmentLineInfos.getG2())) + .setB2(ModificationUtils.getInstance().zeroIfNull(attachmentLineInfos.getB2())); + + CreateLineOnLine algo = new CreateLineOnLineBuilder() + .withPositionPercent(modificationInfos.getPercent()) + .withBusbarSectionOrBusId(modificationInfos.getBbsOrBusId()) + .withFictitiousVoltageLevelId(modificationInfos.getAttachmentPointId()) + .withFictitiousVoltageLevelName(modificationInfos.getAttachmentPointName()) + .withCreateFictitiousSubstation(true) + .withFictitiousSubstationId(modificationInfos.getAttachmentPointId() + "_substation") + .withLine1Id(modificationInfos.getNewLine1Id()) + .withLine1Name(modificationInfos.getNewLine1Name()) + .withLine2Id(modificationInfos.getNewLine2Id()) + .withLine2Name(modificationInfos.getNewLine2Name()) + .withLine(network.getLine(modificationInfos.getLineToAttachToId())) + .withLineAdder(lineAdder) + .build(); + + algo.apply(network, true, subReportNode); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/LineCreation.java b/src/main/java/org/gridsuite/modification/modifications/LineCreation.java new file mode 100644 index 0000000..23182c2 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/LineCreation.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.modification.topology.CreateBranchFeederBays; +import com.powsybl.iidm.modification.topology.CreateBranchFeederBaysBuilder; +import com.powsybl.iidm.network.*; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.CurrentLimitsInfos; +import org.gridsuite.modification.dto.LineCreationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import static org.gridsuite.modification.NetworkModificationException.Type.*; + +/** + * @author Slimane Amar + */ +public class LineCreation extends AbstractModification { + + private final LineCreationInfos modificationInfos; + + public LineCreation(LineCreationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (network.getLine(modificationInfos.getEquipmentId()) != null) { + throw new NetworkModificationException(LINE_ALREADY_EXISTS, modificationInfos.getEquipmentId()); + } + ModificationUtils.getInstance().controlBranchCreation(network, + modificationInfos.getVoltageLevelId1(), modificationInfos.getBusOrBusbarSectionId1(), modificationInfos.getConnectionPosition1(), + modificationInfos.getVoltageLevelId2(), modificationInfos.getBusOrBusbarSectionId2(), modificationInfos.getConnectionPosition2()); + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + VoltageLevel voltageLevel1 = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId1()); + VoltageLevel voltageLevel2 = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId2()); + + if (voltageLevel1.getTopologyKind() == TopologyKind.NODE_BREAKER && + voltageLevel2.getTopologyKind() == TopologyKind.NODE_BREAKER) { + LineAdder lineAdder = ModificationUtils.getInstance().createLineAdder(network, voltageLevel1, voltageLevel2, modificationInfos, false, false); + var position1 = ModificationUtils.getInstance().getPosition(modificationInfos.getConnectionPosition1(), modificationInfos.getBusOrBusbarSectionId1(), network, voltageLevel1); + var position2 = ModificationUtils.getInstance().getPosition(modificationInfos.getConnectionPosition2(), modificationInfos.getBusOrBusbarSectionId2(), network, voltageLevel2); + + CreateBranchFeederBays algo = new CreateBranchFeederBaysBuilder() + .withBusOrBusbarSectionId1(modificationInfos.getBusOrBusbarSectionId1()) + .withBusOrBusbarSectionId2(modificationInfos.getBusOrBusbarSectionId2()) + .withFeederName1(modificationInfos.getConnectionName1() != null ? modificationInfos.getConnectionName1() : modificationInfos.getEquipmentId()) + .withFeederName2(modificationInfos.getConnectionName2() != null ? modificationInfos.getConnectionName2() : modificationInfos.getEquipmentId()) + .withDirection1(modificationInfos.getConnectionDirection1()) + .withDirection2(modificationInfos.getConnectionDirection2()) + .withPositionOrder1(position1) + .withPositionOrder2(position2) + .withBranchAdder(lineAdder).build(); + algo.apply(network, true, subReportNode); + } else { + addLine(network, voltageLevel1, voltageLevel2, modificationInfos, true, true, subReportNode); + } + + // Set permanent and temporary current limits + CurrentLimitsInfos currentLimitsInfos1 = modificationInfos.getCurrentLimits1(); + CurrentLimitsInfos currentLimitsInfos2 = modificationInfos.getCurrentLimits2(); + if (currentLimitsInfos1 != null || currentLimitsInfos2 != null) { + var line = ModificationUtils.getInstance().getLine(network, modificationInfos.getEquipmentId()); + ModificationUtils.getInstance().setCurrentLimits(currentLimitsInfos1, line.newCurrentLimits1()); + ModificationUtils.getInstance().setCurrentLimits(currentLimitsInfos2, line.newCurrentLimits2()); + } + ModificationUtils.getInstance().disconnectBranch(modificationInfos, network.getLine(modificationInfos.getEquipmentId()), subReportNode); + // properties + Line line = network.getLine(modificationInfos.getEquipmentId()); + PropertiesUtils.applyProperties(line, subReportNode, modificationInfos.getProperties(), "LineProperties"); + } + + private void addLine(Network network, VoltageLevel voltageLevel1, VoltageLevel voltageLevel2, LineCreationInfos lineCreationInfos, boolean withSwitch1, boolean withSwitch2, ReportNode subReportNode) { + ModificationUtils.getInstance().createLineAdder(network, voltageLevel1, voltageLevel2, lineCreationInfos, withSwitch1, withSwitch2).add(); + + subReportNode.newReportNode() + .withMessageTemplate("lineCreated", "New line with id=${id} created") + .withUntypedValue("id", lineCreationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + +} diff --git a/src/main/java/org/gridsuite/modification/modifications/LineModification.java b/src/main/java/org/gridsuite/modification/modifications/LineModification.java new file mode 100644 index 0000000..b371277 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/LineModification.java @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.Branch; +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.Network; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.BranchModificationInfos; +import org.gridsuite.modification.dto.LineModificationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import static org.gridsuite.modification.NetworkModificationException.Type.LINE_NOT_FOUND; +import static org.gridsuite.modification.utils.ModificationUtils.insertReportNode; + +/** + * @author Ayoub Labidi + */ +public class LineModification extends AbstractBranchModification { + + public LineModification(LineModificationInfos modificationInfos) { + super(modificationInfos); + } + + @Override + public void check(Network network) throws NetworkModificationException { + Line line = network.getLine(modificationInfos.getEquipmentId()); + if (line == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, + "Line " + modificationInfos.getEquipmentId() + " does not exist in network"); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + Line line = network.getLine(modificationInfos.getEquipmentId()); + // modify the line in the network + modifyLine(line, modificationInfos, subReportNode); + } + + private void modifyLine(Line line, BranchModificationInfos lineModificationInfos, ReportNode subReportNode) { + modifyBranch(line, lineModificationInfos, subReportNode, "lineModification", "Line with id=${id} modified :"); + PropertiesUtils.applyProperties(line, subReportNode, modificationInfos.getProperties(), "LineProperties"); + } + + @Override + protected void modifyCharacteristics(Branch branch, BranchModificationInfos branchModificationInfos, ReportNode subReportNode) { + Line line = (Line) branch; + ReportNode characteristicsReporter = subReportNode.newReportNode().withMessageTemplate("characteristics", "Characteristics").add(); + if (branchModificationInfos.getR() != null && branchModificationInfos.getR().getValue() != null) { + insertReportNode(characteristicsReporter, ModificationUtils.getInstance().buildModificationReport(line.getR(), + branchModificationInfos.getR().getValue(), "Series resistance", 1)); + line.setR(branchModificationInfos.getR().getValue()); + } + if (branchModificationInfos.getX() != null && branchModificationInfos.getX().getValue() != null) { + insertReportNode(characteristicsReporter, ModificationUtils.getInstance().buildModificationReport(line.getX(), + branchModificationInfos.getX().getValue(), "Series reactance", 1)); + line.setX(branchModificationInfos.getX().getValue()); + } + + LineModificationInfos lineModificationInfos = (LineModificationInfos) branchModificationInfos; + modifySide1Characteristics(line, lineModificationInfos, characteristicsReporter); + modifySide2Characteristics(line, lineModificationInfos, characteristicsReporter); + + } + + private void modifySide1Characteristics(Line line, LineModificationInfos lineModificationInfos, + ReportNode characteristicsReportNode) { + if (lineModificationInfos.getG1() != null && lineModificationInfos.getG1().getValue() != null + || lineModificationInfos.getB1() != null && lineModificationInfos.getB1().getValue() != null) { + ReportNode side1ReportNode = characteristicsReportNode.newReportNode().withMessageTemplate("side1Characteristics", "Side 1").add(); + if (lineModificationInfos.getG1() != null && lineModificationInfos.getG1().getValue() != null) { + //convert reported value from siemens to microsiemens + double shuntConductance1ToReport = lineModificationInfos.getG1().getValue() * Math.pow(10, 6); + double oldShuntConductance1ToReport = line.getG1() * Math.pow(10, 6); + insertReportNode(side1ReportNode, ModificationUtils.getInstance().buildModificationReport(oldShuntConductance1ToReport, + shuntConductance1ToReport, "Shunt conductance", 2)); + line.setG1(lineModificationInfos.getG1().getValue()); + } + if (lineModificationInfos.getB1() != null && lineModificationInfos.getB1().getValue() != null) { + //convert reported value from siemens to microsiemens + double shuntSusceptance1ToReport = lineModificationInfos.getB1().getValue() * Math.pow(10, 6); + double oldShuntSusceptance1ToReport = line.getB1() * Math.pow(10, 6); + insertReportNode(side1ReportNode, ModificationUtils.getInstance().buildModificationReport(oldShuntSusceptance1ToReport, + shuntSusceptance1ToReport, "Shunt susceptance", 2)); + line.setB1(lineModificationInfos.getB1().getValue()); + } + } + } + + private void modifySide2Characteristics(Line line, LineModificationInfos lineModificationInfos, + ReportNode characteristicsReportNode) { + if (lineModificationInfos.getG2() != null && lineModificationInfos.getG2().getValue() != null + || lineModificationInfos.getB2() != null && lineModificationInfos.getB2().getValue() != null) { + ReportNode side2Reporter = characteristicsReportNode.newReportNode().withMessageTemplate("side2Characteristics", "Side 2").add(); + if (lineModificationInfos.getG2() != null && lineModificationInfos.getG2().getValue() != null) { + // convert reported value from siemens to microsiemens + double shuntConductance2ToReport = lineModificationInfos.getG2().getValue() * Math.pow(10, 6); + double oldShuntConductance2ToReport = line.getG2() * Math.pow(10, 6); + insertReportNode(side2Reporter, ModificationUtils.getInstance().buildModificationReport(oldShuntConductance2ToReport, + shuntConductance2ToReport, "Shunt conductance", 2)); + line.setG2(lineModificationInfos.getG2().getValue()); + } + if (lineModificationInfos.getB2() != null && lineModificationInfos.getB2().getValue() != null) { + // convert reported value from siemens to microsiemens + double shuntSusceptance2ToReport = lineModificationInfos.getB2().getValue() * Math.pow(10, 6); + double oldShuntSusceptance2ToReport = line.getB2() * Math.pow(10, 6); + insertReportNode(side2Reporter, ModificationUtils.getInstance().buildModificationReport(oldShuntSusceptance2ToReport, + shuntSusceptance2ToReport, "Shunt susceptance", 2)); + line.setB2(lineModificationInfos.getB2().getValue()); + } + } + } + + @Override + protected boolean characteristicsModified(BranchModificationInfos branchModificationInfos) { + LineModificationInfos lineModificationInfos = (LineModificationInfos) branchModificationInfos; + return super.characteristicsModified(branchModificationInfos) + || lineModificationInfos.getG1() != null + && lineModificationInfos.getG1().getValue() != null + || lineModificationInfos.getB1() != null + && lineModificationInfos.getB1().getValue() != null + || lineModificationInfos.getG2() != null + && lineModificationInfos.getG2().getValue() != null + || lineModificationInfos.getB2() != null + && lineModificationInfos.getB2().getValue() != null; + } + +} diff --git a/src/main/java/org/gridsuite/modification/modifications/LineSplitWithVoltageLevel.java b/src/main/java/org/gridsuite/modification/modifications/LineSplitWithVoltageLevel.java new file mode 100644 index 0000000..9806f27 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/LineSplitWithVoltageLevel.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.modification.topology.ConnectVoltageLevelOnLine; +import com.powsybl.iidm.modification.topology.ConnectVoltageLevelOnLineBuilder; +import com.powsybl.iidm.network.*; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.LineSplitWithVoltageLevelInfos; +import org.gridsuite.modification.dto.VoltageLevelCreationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.springframework.lang.NonNull; + +import static org.gridsuite.modification.NetworkModificationException.Type.*; + +/** + * @author Slimane Amar + */ +public class LineSplitWithVoltageLevel extends AbstractModification { + + private final LineSplitWithVoltageLevelInfos modificationInfos; + + public LineSplitWithVoltageLevel(LineSplitWithVoltageLevelInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(@NonNull Network network) throws NetworkModificationException { + if (network.getLine(modificationInfos.getLineToSplitId()) == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, modificationInfos.getLineToSplitId()); + } + ModificationUtils.getInstance().controlNewOrExistingVoltageLevel(modificationInfos.getMayNewVoltageLevelInfos(), + modificationInfos.getExistingVoltageLevelId(), modificationInfos.getBbsOrBusId(), network); + // check future lines don't exist + if (network.getLine(modificationInfos.getNewLine1Id()) != null) { + throw new NetworkModificationException(LINE_ALREADY_EXISTS, modificationInfos.getNewLine1Id()); + } + if (network.getLine(modificationInfos.getNewLine2Id()) != null) { + throw new NetworkModificationException(LINE_ALREADY_EXISTS, modificationInfos.getNewLine2Id()); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + VoltageLevelCreationInfos mayNewVL = modificationInfos.getMayNewVoltageLevelInfos(); + if (mayNewVL != null) { + ModificationUtils.getInstance().createVoltageLevel(mayNewVL, subReportNode, network); + } + + ConnectVoltageLevelOnLine algo = new ConnectVoltageLevelOnLineBuilder() + .withPositionPercent(modificationInfos.getPercent()) + .withBusbarSectionOrBusId(modificationInfos.getBbsOrBusId()) + .withLine1Id(modificationInfos.getNewLine1Id()) + .withLine1Name(modificationInfos.getNewLine1Name()) + .withLine2Id(modificationInfos.getNewLine2Id()) + .withLine2Name(modificationInfos.getNewLine2Name()) + .withLine(network.getLine(modificationInfos.getLineToSplitId())) + .build(); + + algo.apply(network, true, subReportNode); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/LinesAttachToSplitLines.java b/src/main/java/org/gridsuite/modification/modifications/LinesAttachToSplitLines.java new file mode 100644 index 0000000..b3a3cd5 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/LinesAttachToSplitLines.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.modification.topology.ReplaceTeePointByVoltageLevelOnLine; +import com.powsybl.iidm.modification.topology.ReplaceTeePointByVoltageLevelOnLineBuilder; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.VoltageLevel; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.LinesAttachToSplitLinesInfos; +import org.gridsuite.modification.utils.ModificationUtils; + +import static org.gridsuite.modification.NetworkModificationException.Type.LINE_ALREADY_EXISTS; +import static org.gridsuite.modification.NetworkModificationException.Type.LINE_NOT_FOUND; + +/** + * @author David Braquart + */ +public class LinesAttachToSplitLines extends AbstractModification { + + private final LinesAttachToSplitLinesInfos modificationInfos; + + public LinesAttachToSplitLines(LinesAttachToSplitLinesInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + // check existing lines, vl and busbar + if (network.getLine(modificationInfos.getLineToAttachTo1Id()) == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, modificationInfos.getLineToAttachTo1Id()); + } + if (network.getLine(modificationInfos.getLineToAttachTo2Id()) == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, modificationInfos.getLineToAttachTo2Id()); + } + if (network.getLine(modificationInfos.getAttachedLineId()) == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, modificationInfos.getAttachedLineId()); + } + VoltageLevel vl = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId()); + ModificationUtils.getInstance().controlBus(network, vl, modificationInfos.getBbsBusId()); + // check future lines don't exist + if (network.getLine(modificationInfos.getReplacingLine1Id()) != null) { + throw new NetworkModificationException(LINE_ALREADY_EXISTS, modificationInfos.getReplacingLine1Id()); + } + if (network.getLine(modificationInfos.getReplacingLine2Id()) != null) { + throw new NetworkModificationException(LINE_ALREADY_EXISTS, modificationInfos.getReplacingLine2Id()); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + ReplaceTeePointByVoltageLevelOnLine algo = new ReplaceTeePointByVoltageLevelOnLineBuilder() + .withTeePointLine1(modificationInfos.getLineToAttachTo1Id()) + .withTeePointLine2(modificationInfos.getLineToAttachTo2Id()) + .withTeePointLineToRemove(modificationInfos.getAttachedLineId()) + .withBbsOrBusId(modificationInfos.getBbsBusId()) + .withNewLine1Id(modificationInfos.getReplacingLine1Id()) + .withNewLine1Name(modificationInfos.getReplacingLine1Name()) + .withNewLine2Id(modificationInfos.getReplacingLine2Id()) + .withNewLine2Name(modificationInfos.getReplacingLine2Name()) + .build(); + algo.apply(network, true, subReportNode); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/LoadCreation.java b/src/main/java/org/gridsuite/modification/modifications/LoadCreation.java new file mode 100644 index 0000000..03c7fdd --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/LoadCreation.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.LoadCreationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import static org.gridsuite.modification.NetworkModificationException.Type.LOAD_ALREADY_EXISTS; +import static org.gridsuite.modification.utils.ModificationUtils.createInjectionInNodeBreaker; + +/** + * @author Slimane Amar + */ +public class LoadCreation extends AbstractModification { + + private final LoadCreationInfos modificationInfos; + + public LoadCreation(LoadCreationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (network.getLoad(modificationInfos.getEquipmentId()) != null) { + throw new NetworkModificationException(LOAD_ALREADY_EXISTS, modificationInfos.getEquipmentId()); + } + ModificationUtils.getInstance().controlConnectivity(network, modificationInfos.getVoltageLevelId(), + modificationInfos.getBusOrBusbarSectionId(), modificationInfos.getConnectionPosition()); + } + + @Override + public void apply(Network network, ReportNode subReporter) { + // create the load in the network + VoltageLevel voltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId()); + if (voltageLevel.getTopologyKind() == TopologyKind.NODE_BREAKER) { + LoadAdder loadAdder = createLoadAdderInNodeBreaker(voltageLevel, modificationInfos); + createInjectionInNodeBreaker(voltageLevel, modificationInfos, network, loadAdder, subReporter); + } else { + createLoadInBusBreaker(voltageLevel, modificationInfos); + subReporter.newReportNode() + .withMessageTemplate("loadCreated", "New load with id=${id} created") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + reportElementaryCreations(subReporter); + ModificationUtils.getInstance().disconnectCreatedInjection(modificationInfos, network.getLoad(modificationInfos.getEquipmentId()), subReporter); + + // properties + Load load = network.getLoad(modificationInfos.getEquipmentId()); + PropertiesUtils.applyProperties(load, subReporter, modificationInfos.getProperties(), "LoadProperties"); + } + + private void reportElementaryCreations(ReportNode subReportNode) { + if (modificationInfos.getEquipmentName() != null) { + ModificationUtils.getInstance() + .reportElementaryCreation(subReportNode, modificationInfos.getEquipmentName(), "Name"); + } + + if (modificationInfos.getLoadType() != null) { + ModificationUtils.getInstance() + .reportElementaryCreation(subReportNode, modificationInfos.getLoadType(), "Type"); + } + + ModificationUtils.getInstance() + .reportElementaryCreation(subReportNode, modificationInfos.getP0(), "Active power"); + + ModificationUtils.getInstance() + .reportElementaryCreation(subReportNode, modificationInfos.getQ0(), "Reactive power"); + } + + private LoadAdder createLoadAdderInNodeBreaker(VoltageLevel voltageLevel, LoadCreationInfos loadCreationInfos) { + // creating the load adder + return voltageLevel.newLoad() + .setId(loadCreationInfos.getEquipmentId()) + .setName(loadCreationInfos.getEquipmentName()) + .setLoadType(loadCreationInfos.getLoadType()) + .setP0(loadCreationInfos.getP0()) + .setQ0(loadCreationInfos.getQ0()); + } + + private Load createLoadInBusBreaker(VoltageLevel voltageLevel, LoadCreationInfos loadCreationInfos) { + Bus bus = ModificationUtils.getInstance().getBusBreakerBus(voltageLevel, loadCreationInfos.getBusOrBusbarSectionId()); + + // creating the load + return voltageLevel.newLoad() + .setId(loadCreationInfos.getEquipmentId()) + .setName(loadCreationInfos.getEquipmentName()) + .setLoadType(loadCreationInfos.getLoadType()) + .setBus(bus.getId()) + .setConnectableBus(bus.getId()) + .setP0(loadCreationInfos.getP0()) + .setQ0(loadCreationInfos.getQ0()).add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/LoadModification.java b/src/main/java/org/gridsuite/modification/modifications/LoadModification.java new file mode 100644 index 0000000..446b576 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/LoadModification.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import com.powsybl.iidm.network.extensions.ConnectablePositionAdder; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.AttributeModification; +import org.gridsuite.modification.dto.LoadModificationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import static org.gridsuite.modification.NetworkModificationException.Type.LOAD_NOT_FOUND; + +/** + * @author Ayoub Labidi + */ +public class LoadModification extends AbstractModification { + + private final LoadModificationInfos modificationInfos; + + public LoadModification(LoadModificationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + Load load = network.getLoad(modificationInfos.getEquipmentId()); + if (load == null) { + throw new NetworkModificationException(LOAD_NOT_FOUND, + "Load " + modificationInfos.getEquipmentId() + " does not exist in network"); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + Load load = network.getLoad(modificationInfos.getEquipmentId()); + // modify the load in the network + modifyLoad(load, subReportNode); + } + + private void modifyLoad(Load load, ReportNode subReportNode) { + subReportNode.newReportNode() + .withMessageTemplate("loadModification", "Load with id=${id} modified :") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + + ModificationUtils.getInstance().applyElementaryModifications(load::setName, () -> load.getOptionalName().orElse("No value"), modificationInfos.getEquipmentName(), subReportNode, "Name"); + ModificationUtils.getInstance().applyElementaryModifications(load::setLoadType, load::getLoadType, modificationInfos.getLoadType(), subReportNode, "Type"); + modifyP0(load, modificationInfos.getP0(), subReportNode); + modifyQ0(load, modificationInfos.getQ0(), subReportNode); + modifyLoadConnectivityAttributes(modificationInfos, load, subReportNode); + // properties + PropertiesUtils.applyProperties(load, subReportNode, modificationInfos.getProperties(), "LoadProperties"); + } + + public static void modifyQ0(Load load, AttributeModification q0, ReportNode subReportNode) { + ModificationUtils.getInstance().applyElementaryModifications(load::setQ0, load::getQ0, q0, subReportNode, "Constant reactive power"); + } + + public static void modifyP0(Load load, AttributeModification p0, ReportNode subReportNode) { + ModificationUtils.getInstance().applyElementaryModifications(load::setP0, load::getP0, p0, subReportNode, "Constant active power"); + } + + private ReportNode modifyLoadConnectivityAttributes(LoadModificationInfos modificationInfos, + Load load, ReportNode subReportNode) { + ConnectablePosition connectablePosition = load.getExtension(ConnectablePosition.class); + ConnectablePositionAdder connectablePositionAdder = load.newExtension(ConnectablePositionAdder.class); + return ModificationUtils.getInstance().modifyInjectionConnectivityAttributes(connectablePosition, connectablePositionAdder, load, modificationInfos, subReportNode); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/LoadScaling.java b/src/main/java/org/gridsuite/modification/modifications/LoadScaling.java new file mode 100644 index 0000000..8c037a6 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/LoadScaling.java @@ -0,0 +1,152 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.modification.scalable.Scalable; +import com.powsybl.iidm.modification.scalable.ScalingParameters; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.VariationMode; +import org.gridsuite.modification.VariationType; +import org.gridsuite.modification.dto.IdentifiableAttributes; +import org.gridsuite.modification.dto.LoadScalingInfos; +import org.gridsuite.modification.dto.ScalingVariationInfos; +import org.gridsuite.modification.utils.ModificationUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +/** + * @author bendaamerahm + */ +public class LoadScaling extends AbstractScaling { + + public LoadScaling(LoadScalingInfos loadScalableInfos) { + super(loadScalableInfos); + } + + @Override + protected void applyVentilationVariation(Network network, ReportNode subReportNode, List identifiableAttributes, ScalingVariationInfos scalingVariationInfos, Double distributionKeys) { + if (distributionKeys != null) { + AtomicReference sum = new AtomicReference<>(0D); + List percentages = new ArrayList<>(); + List scalables = new ArrayList<>(); + + identifiableAttributes.forEach(equipment -> { + Load load = network.getLoad(equipment.getId()); + if (ModificationUtils.isInjectionConnected(load)) { + sum.set(load.getP0() + sum.get()); + scalables.add(getScalable(equipment.getId())); + percentages.add((equipment.getDistributionKey() / distributionKeys) * 100); + } + }); + Scalable ventilationScalable = Scalable.proportional(percentages, scalables); + var asked = getAsked(scalingVariationInfos, sum); + var done = scale(network, scalingVariationInfos, asked, ventilationScalable); + reportScaling(subReportNode, scalingVariationInfos.getVariationMode(), asked, done); + } + } + + @Override + protected void applyRegularDistributionVariation(Network network, ReportNode subReportNode, List identifiableAttributes, ScalingVariationInfos scalingVariationInfos) { + List loads = identifiableAttributes + .stream() + .map(attribute -> network.getLoad(attribute.getId())) + .filter(ModificationUtils::isInjectionConnected) + .toList(); + + AtomicReference sum = new AtomicReference<>(0D); + + List scalables = loads.stream() + .map(load -> { + sum.set(sum.get() + load.getP0()); + return getScalable(load.getId()); + }).collect(Collectors.toList()); + + List percentages = new ArrayList<>(Collections.nCopies(scalables.size(), 100.0 / scalables.size())); + Scalable regularDistributionScalable = Scalable.proportional(percentages, scalables); + var asked = getAsked(scalingVariationInfos, sum); + var done = scale(network, scalingVariationInfos, asked, regularDistributionScalable); + reportScaling(subReportNode, scalingVariationInfos.getVariationMode(), asked, done); + } + + @Override + protected void applyProportionalVariation(Network network, ReportNode subReportNode, List identifiableAttributes, ScalingVariationInfos scalingVariationInfos) { + List loads = identifiableAttributes.stream() + .map(attribute -> network.getLoad(attribute.getId())) + .filter(ModificationUtils::isInjectionConnected) + .toList(); + AtomicReference sum = new AtomicReference<>(0D); + Map targetPMap = new HashMap<>(); + List percentages = new ArrayList<>(); + List scalables = new ArrayList<>(); + loads.forEach(load -> { + targetPMap.put(load.getId(), load.getP0()); + sum.set(sum.get() + load.getP0()); + }); + targetPMap.forEach((id, p) -> { + percentages.add((p / sum.get()) * 100); + scalables.add(getScalable(id)); + }); + + Scalable proportionalScalable = Scalable.proportional(percentages, scalables); + var asked = getAsked(scalingVariationInfos, sum); + var done = scale(network, scalingVariationInfos, asked, proportionalScalable); + reportScaling(subReportNode, scalingVariationInfos.getVariationMode(), asked, done); + } + + @Override + protected void applyProportionalToPmaxVariation(Network network, ReportNode subReportNode, List identifiableAttributes, ScalingVariationInfos scalingVariationInfos) { + // no implementation for load scaling + throw new NetworkModificationException(scalingInfos.getErrorType(), String.format("This variation mode is not supported : %s", scalingVariationInfos.getVariationMode().name())); + } + + @Override + protected void applyStackingUpVariation(Network network, ReportNode subReportNode, List identifiableAttributes, ScalingVariationInfos scalingVariationInfos) { + // no implementation for load scaling + throw new NetworkModificationException(scalingInfos.getErrorType(), String.format("This variation mode is not supported : %s", scalingVariationInfos.getVariationMode().name())); + } + + private double scale(Network network, ScalingVariationInfos scalingVariationInfos, double asked, Scalable proportionalScalable) { + return switch (scalingVariationInfos.getReactiveVariationMode()) { + case CONSTANT_Q -> + proportionalScalable.scale(network, asked, new ScalingParameters().setScalingConvention(Scalable.ScalingConvention.LOAD)); + case TAN_PHI_FIXED -> + proportionalScalable.scale(network, asked, new ScalingParameters().setScalingConvention(Scalable.ScalingConvention.LOAD).setConstantPowerFactor(true)); + }; + } + + @Override + public double getAsked(ScalingVariationInfos scalingVariationInfos, AtomicReference sum) { + return scalingInfos.getVariationType() == VariationType.DELTA_P + ? scalingVariationInfos.getVariationValue() + : scalingVariationInfos.getVariationValue() - sum.get(); + } + + @Override + protected Scalable getScalable(String id) { + return Scalable.onLoad(id, -Double.MAX_VALUE, Double.MAX_VALUE); + } + + private void reportScaling(ReportNode subReportNode, VariationMode variationMode, double askedValue, double actualValue) { + subReportNode.newReportNode() + .withMessageTemplate("scalingApplied", "Successfully scaling variation in ${variationMode} mode with variation value asked is ${askedValue} and variation done is ${actualValue}") + .withUntypedValue("variationMode", variationMode.name()) + .withUntypedValue("askedValue", askedValue) + .withUntypedValue("actualValue", actualValue) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/OperatingStatusModification.java b/src/main/java/org/gridsuite/modification/modifications/OperatingStatusModification.java new file mode 100644 index 0000000..46767ac --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/OperatingStatusModification.java @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.modification.tripping.BranchTripping; +import com.powsybl.iidm.modification.tripping.BusbarSectionTripping; +import com.powsybl.iidm.modification.tripping.HvdcLineTripping; +import com.powsybl.iidm.modification.tripping.ThreeWindingsTransformerTripping; +import com.powsybl.iidm.modification.tripping.Tripping; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.OperatingStatus; +import com.powsybl.iidm.network.extensions.OperatingStatusAdder; +import com.powsybl.iidm.network.util.SwitchPredicates; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.OperatingStatusModificationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashSet; +import java.util.Objects; +import java.util.function.Predicate; + +import static org.gridsuite.modification.NetworkModificationException.Type.EQUIPMENT_NOT_FOUND; +import static org.gridsuite.modification.NetworkModificationException.Type.OPERATING_STATUS_MODIFICATION_ERROR; +import static org.gridsuite.modification.utils.ModificationUtils.distinctByKey; + +/** + * @author Ghazwa REHILI + */ +public class OperatingStatusModification extends AbstractModification { + + private final OperatingStatusModificationInfos modificationInfos; + private static final Logger LOGGER = LoggerFactory.getLogger(OperatingStatusModification.class); + + private static final String APPLIED = "Applied"; + + public OperatingStatusModification(OperatingStatusModificationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + String equipmentId = modificationInfos.getEquipmentId(); + Identifiable equipment = network.getIdentifiable(equipmentId); + if (equipment == null) { + throw new NetworkModificationException(EQUIPMENT_NOT_FOUND, equipmentId); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + String equipmentId = modificationInfos.getEquipmentId(); + Identifiable equipment = network.getIdentifiable(equipmentId); + if (equipment == null) { + throw new NetworkModificationException(EQUIPMENT_NOT_FOUND, equipmentId); + } + + String equipmentType = String.valueOf(equipment.getType()); + switch (modificationInfos.getAction()) { + case LOCKOUT -> applyLockoutEquipment(subReportNode, equipment, equipmentType); + case TRIP -> applyTripEquipment(subReportNode, equipment, equipmentType, network); + case SWITCH_ON -> applySwitchOnEquipment(subReportNode, equipment, equipmentType); + case ENERGISE_END_ONE -> + applyEnergiseEquipmentEnd(subReportNode, equipment, equipmentType, TwoSides.ONE); + case ENERGISE_END_TWO -> + applyEnergiseEquipmentEnd(subReportNode, equipment, equipmentType, TwoSides.TWO); + default -> + throw NetworkModificationException.createOperatingActionTypeUnsupported(modificationInfos.getAction()); + } + } + + private void applyLockoutEquipment(ReportNode subReportNode, Identifiable equipment, String equipmentType) { + if (disconnectAllTerminals(equipment)) { + equipment.newExtension(OperatingStatusAdder.class).withStatus(OperatingStatus.Status.PLANNED_OUTAGE).add(); + } else { + throw new NetworkModificationException(OPERATING_STATUS_MODIFICATION_ERROR, "Unable to disconnect all equipment ends"); + } + subReportNode.newReportNode() + .withMessageTemplate("lockout" + equipmentType + APPLIED, "${equipmentType} ${id} (id) : lockout applied") + .withUntypedValue("equipmentType", equipmentType) + .withUntypedValue("id", equipment.getId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + + private void applyTripEquipment(ReportNode subReportNode, Identifiable equipment, String equipmentType, Network network) { + var switchesToDisconnect = new HashSet(); + var terminalsToDisconnect = new HashSet(); + var traversedTerminals = new HashSet(); + getTrippingFromIdentifiable(equipment).traverse(network, switchesToDisconnect, terminalsToDisconnect, traversedTerminals); + + LOGGER.info("Apply Trip on {} {}, switchesToDisconnect: {} terminalsToDisconnect: {} traversedTerminals: {}", + equipmentType, equipment.getId(), + switchesToDisconnect.stream().map(Identifiable::getId).toList(), + terminalsToDisconnect.stream().map(Terminal::getConnectable).map(Identifiable::getId).toList(), + traversedTerminals.stream().map(Terminal::getConnectable).map(Identifiable::getId).toList()); + + switchesToDisconnect.forEach(sw -> sw.setOpen(true)); + terminalsToDisconnect.forEach(Terminal::disconnect); + + subReportNode.newReportNode() + .withMessageTemplate("trip" + equipmentType + APPLIED, "${equipmentType} ${id} (id) : trip applied") + .withUntypedValue("equipmentType", equipmentType) + .withUntypedValue("id", equipment.getId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + + traversedTerminals.stream() + .map(t -> network.getIdentifiable(t.getConnectable().getId())) + .filter(Objects::nonNull) + .filter(distinctByKey(Identifiable::getId)) + .forEach(b -> equipment.newExtension(OperatingStatusAdder.class) + .withStatus(OperatingStatus.Status.FORCED_OUTAGE) + .add()); + } + + private void applySwitchOnEquipment(ReportNode subReportNode, Identifiable equipment, String equipmentType) { + if (!(equipment instanceof Branch)) { + throw NetworkModificationException.createEquipmentTypeNotSupported(equipment.getClass().getSimpleName()); + } + + if (connectAllTerminals(equipment)) { + equipment.newExtension(OperatingStatusAdder.class).withStatus(OperatingStatus.Status.IN_OPERATION).add(); + } else { + throw new NetworkModificationException(OPERATING_STATUS_MODIFICATION_ERROR, "Unable to connect all equipment ends"); + } + + subReportNode.newReportNode() + .withMessageTemplate("switchOn" + equipmentType + APPLIED, "${equipmentType} ${id} (id) : switch on applied") + .withUntypedValue("equipmentType", equipmentType) + .withUntypedValue("id", equipment.getId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + + private void applyEnergiseEquipmentEnd(ReportNode subReportNode, Identifiable equipment, String equipmentType, TwoSides side) { + if (!(equipment instanceof Branch branch)) { + throw NetworkModificationException.createEquipmentTypeNotSupported(equipment.getClass().getSimpleName()); + } + + TwoSides oppositeSide = side == TwoSides.ONE ? TwoSides.TWO : TwoSides.ONE; + if (connectOneTerminal(branch.getTerminal(side)) && disconnectOneTerminal(branch.getTerminal(oppositeSide), SwitchPredicates.IS_CLOSED_BREAKER)) { + branch.newExtension(OperatingStatusAdder.class).withStatus(OperatingStatus.Status.IN_OPERATION).add(); + } else { + throw new NetworkModificationException(OPERATING_STATUS_MODIFICATION_ERROR, "Unable to energise equipment end"); + } + + subReportNode.newReportNode() + .withMessageTemplate("energise" + equipmentType + "EndApplied", "${equipmentType} ${id} (id) : energise the side ${side} applied") + .withUntypedValue("equipmentType", equipmentType) + .withUntypedValue("id", equipment.getId()) + .withUntypedValue("side", side.name()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + + private boolean disconnectAllTerminals(Identifiable equipment) { + return ModificationUtils.getInstance().getTerminalsFromIdentifiable(equipment).stream().allMatch(terminal -> disconnectOneTerminal(terminal, SwitchPredicates.IS_NONFICTIONAL)); + } + + private boolean disconnectOneTerminal(Terminal terminal, Predicate switchPredicates) { + return !terminal.isConnected() || terminal.disconnect(switchPredicates); + } + + private boolean connectAllTerminals(Identifiable equipment) { + return ModificationUtils.getInstance().getTerminalsFromIdentifiable(equipment).stream().allMatch(this::connectOneTerminal); + } + + private boolean connectOneTerminal(Terminal terminal) { + return terminal.isConnected() || terminal.connect(); + } + + public Tripping getTrippingFromIdentifiable(Identifiable identifiable) { + if (identifiable instanceof Branch branch) { + return new BranchTripping(branch.getId()); + } else if (identifiable instanceof BusbarSection bbs) { + return new BusbarSectionTripping(bbs.getId()); + } else if (identifiable instanceof ThreeWindingsTransformer w3t) { + return new ThreeWindingsTransformerTripping(w3t.getId()); + } else if (identifiable instanceof HvdcLine hvdcLine) { + return new HvdcLineTripping(hvdcLine.getId()); + } + throw NetworkModificationException.createEquipmentTypeNotSupported(identifiable.getClass().getSimpleName()); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/ShuntCompensatorCreation.java b/src/main/java/org/gridsuite/modification/modifications/ShuntCompensatorCreation.java new file mode 100644 index 0000000..1a21066 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/ShuntCompensatorCreation.java @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.ShuntCompensatorCreationInfos; +import org.gridsuite.modification.dto.ShuntCompensatorType; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import static org.gridsuite.modification.NetworkModificationException.Type.CREATE_SHUNT_COMPENSATOR_ERROR; +import static org.gridsuite.modification.NetworkModificationException.Type.SHUNT_COMPENSATOR_ALREADY_EXISTS; +import static org.gridsuite.modification.utils.ModificationUtils.createInjectionInNodeBreaker; + +/** + * @author Slimane Amar + */ +public class ShuntCompensatorCreation extends AbstractModification { + + private final ShuntCompensatorCreationInfos modificationInfos; + + public ShuntCompensatorCreation(ShuntCompensatorCreationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (network.getShuntCompensator(modificationInfos.getEquipmentId()) != null) { + throw new NetworkModificationException(SHUNT_COMPENSATOR_ALREADY_EXISTS, modificationInfos.getEquipmentId()); + } + + if (modificationInfos.getMaximumSectionCount() < 1) { + throw new NetworkModificationException(CREATE_SHUNT_COMPENSATOR_ERROR, "Maximum section count should be greater or equal to 1"); + } + + if (modificationInfos.getSectionCount() < 0 || modificationInfos.getSectionCount() > modificationInfos.getMaximumSectionCount()) { + throw new NetworkModificationException(CREATE_SHUNT_COMPENSATOR_ERROR, String.format("Section count should be between 0 and Maximum section count (%d), actual : %d", + modificationInfos.getMaximumSectionCount(), + modificationInfos.getSectionCount())); + } + ModificationUtils.getInstance().controlConnectivity(network, modificationInfos.getVoltageLevelId(), + modificationInfos.getBusOrBusbarSectionId(), modificationInfos.getConnectionPosition()); + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + // create the shunt compensator in the network + VoltageLevel voltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId()); + if (modificationInfos.getMaxSusceptance() == null) { + double maxSusceptance = (modificationInfos.getMaxQAtNominalV()) / Math.pow(voltageLevel.getNominalV(), 2); + modificationInfos.setMaxSusceptance( + modificationInfos.getShuntCompensatorType() == ShuntCompensatorType.CAPACITOR + ? maxSusceptance + : -maxSusceptance); + } + if (voltageLevel.getTopologyKind() == TopologyKind.NODE_BREAKER) { + ShuntCompensatorAdder shuntCompensatorAdder = createShuntAdderInNodeBreaker(voltageLevel, modificationInfos); + createInjectionInNodeBreaker(voltageLevel, modificationInfos, network, shuntCompensatorAdder, subReportNode); + } else { + createShuntInBusBreaker(voltageLevel, modificationInfos); + subReportNode.newReportNode() + .withMessageTemplate("shuntCompensatorCreated", "New shunt compensator with id=${id} created") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + ModificationUtils.getInstance().disconnectCreatedInjection(modificationInfos, network.getShuntCompensator(modificationInfos.getEquipmentId()), subReportNode); + // properties + ShuntCompensator shuntCompensator = network.getShuntCompensator(modificationInfos.getEquipmentId()); + PropertiesUtils.applyProperties(shuntCompensator, subReportNode, modificationInfos.getProperties(), "ShuntCompensatorProperties"); + } + + private ShuntCompensatorAdder createShuntAdderInNodeBreaker(VoltageLevel voltageLevel, ShuntCompensatorCreationInfos shuntCompensatorInfos) { + // creating the shunt compensator + ShuntCompensatorAdder shuntAdder = voltageLevel.newShuntCompensator() + .setId(shuntCompensatorInfos.getEquipmentId()) + .setName(shuntCompensatorInfos.getEquipmentName()) + .setSectionCount(shuntCompensatorInfos.getSectionCount()); + + /* when we create non-linear shunt, this is where we branch ;) */ + shuntAdder.newLinearModel() + .setBPerSection(shuntCompensatorInfos.getMaxSusceptance() / shuntCompensatorInfos.getMaximumSectionCount()) + .setMaximumSectionCount(shuntCompensatorInfos.getMaximumSectionCount()) + .add(); + + return shuntAdder; + } + + private void createShuntInBusBreaker(VoltageLevel voltageLevel, ShuntCompensatorCreationInfos shuntCompensatorInfos) { + Bus bus = ModificationUtils.getInstance().getBusBreakerBus(voltageLevel, shuntCompensatorInfos.getBusOrBusbarSectionId()); + /* creating the shunt compensator */ + voltageLevel.newShuntCompensator() + .setId(shuntCompensatorInfos.getEquipmentId()) + .setName(shuntCompensatorInfos.getEquipmentName()) + .setSectionCount(shuntCompensatorInfos.getSectionCount()) + .setBus(bus.getId()) + .setConnectableBus(bus.getId()) + .newLinearModel() + .setBPerSection(shuntCompensatorInfos.getMaxSusceptance() / shuntCompensatorInfos.getMaximumSectionCount()) + .setMaximumSectionCount(shuntCompensatorInfos.getMaximumSectionCount()) + .add() + .add(); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/ShuntCompensatorModification.java b/src/main/java/org/gridsuite/modification/modifications/ShuntCompensatorModification.java new file mode 100644 index 0000000..503e3ee --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/ShuntCompensatorModification.java @@ -0,0 +1,234 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import com.powsybl.iidm.network.extensions.ConnectablePositionAdder; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.AttributeModification; +import org.gridsuite.modification.dto.ShuntCompensatorModificationInfos; +import org.gridsuite.modification.dto.ShuntCompensatorType; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import java.util.ArrayList; +import java.util.List; + +import static org.gridsuite.modification.NetworkModificationException.Type.MODIFY_SHUNT_COMPENSATOR_ERROR; +import static org.gridsuite.modification.NetworkModificationException.Type.SHUNT_COMPENSATOR_NOT_FOUND; +import static org.gridsuite.modification.utils.ModificationUtils.insertReportNode; + +/** + * @author Seddik Yengui + */ + +public class ShuntCompensatorModification extends AbstractModification { + private static final String SWITCHED_ON_Q_AT_NOMINALV_LOG_MESSAGE = "Switched-on Q at nominal voltage"; + + private final ShuntCompensatorModificationInfos modificationInfos; + + public ShuntCompensatorModification(ShuntCompensatorModificationInfos shuntCompensatorModificationInfos) { + this.modificationInfos = shuntCompensatorModificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + ShuntCompensator shuntCompensator = network.getShuntCompensator(modificationInfos.getEquipmentId()); + if (shuntCompensator == null) { + throw new NetworkModificationException(SHUNT_COMPENSATOR_NOT_FOUND, + String.format("Shunt compensator %s does not exist in network", modificationInfos.getEquipmentId())); + } + + int maximumSectionCount = modificationInfos.getMaximumSectionCount() != null + ? modificationInfos.getMaximumSectionCount().getValue() + : shuntCompensator.getMaximumSectionCount(); + + int sectionCount = modificationInfos.getSectionCount() != null + ? modificationInfos.getSectionCount().getValue() + : shuntCompensator.getSectionCount(); + + if (modificationInfos.getMaximumSectionCount() != null && modificationInfos.getMaximumSectionCount().getValue() < 1) { + throw new NetworkModificationException(MODIFY_SHUNT_COMPENSATOR_ERROR, "Maximum section count should be greater or equal to 1"); + } + + if (sectionCount < 0 || maximumSectionCount < 1 || sectionCount > maximumSectionCount) { + throw new NetworkModificationException(MODIFY_SHUNT_COMPENSATOR_ERROR, String.format("Section count should be between 0 and Maximum section count (%d), actual : %d", maximumSectionCount, sectionCount)); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + ShuntCompensator shuntCompensator = network.getShuntCompensator(modificationInfos.getEquipmentId()); + VoltageLevel voltageLevel = shuntCompensator.getTerminal().getVoltageLevel(); + + subReportNode.newReportNode() + .withMessageTemplate("shuntCompensatorModification", "Shunt Compensator with id=${id} modified :") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + + ModificationUtils.getInstance().applyElementaryModifications(shuntCompensator::setName, () -> shuntCompensator.getOptionalName().orElse("No value"), modificationInfos.getEquipmentName(), subReportNode, "Name"); + + if (shuntCompensator.getModelType() == ShuntCompensatorModelType.LINEAR) { + applyModificationOnLinearModel(subReportNode, shuntCompensator, voltageLevel); + } + modifyShuntCompensatorConnectivityAttributes(modificationInfos, shuntCompensator, subReportNode); + PropertiesUtils.applyProperties(shuntCompensator, subReportNode, modificationInfos.getProperties(), "ShuntCompensatorProperties"); + } + + public static void modifyMaximumSectionCount(AttributeModification maximumSectionCountModif, + AttributeModification maxSusceptance, + AttributeModification maxQAtNominalV, + List reports, + ShuntCompensator shuntCompensator, + ShuntCompensatorLinearModel model) { + if (maximumSectionCountModif != null) { + var maximumSectionCount = maximumSectionCountModif.getValue(); + if (maxSusceptance == null && maxQAtNominalV == null) { + model.setBPerSection(model.getBPerSection() * shuntCompensator.getMaximumSectionCount() / maximumSectionCount); + } + if (reports != null) { + reports.add(ModificationUtils.getInstance().buildModificationReport(shuntCompensator.getMaximumSectionCount(), maximumSectionCount, "Maximum section count")); + } + model.setMaximumSectionCount(maximumSectionCount); + } + } + + public static void modifySectionCount(AttributeModification sectionCount, List reports, ShuntCompensator shuntCompensator) { + if (sectionCount != null) { + var newSectionCount = sectionCount.getValue(); + if (reports != null) { + reports.add(ModificationUtils.getInstance().buildModificationReport(shuntCompensator.getSectionCount(), newSectionCount, "Section count")); + } + shuntCompensator.setSectionCount(newSectionCount); + } + } + + private void applyModificationOnLinearModel(ReportNode subReportNode, ShuntCompensator shuntCompensator, VoltageLevel voltageLevel) { + List reports = new ArrayList<>(); + ShuntCompensatorLinearModel model = shuntCompensator.getModel(ShuntCompensatorLinearModel.class); + var shuntCompensatorType = model.getBPerSection() > 0 ? ShuntCompensatorType.CAPACITOR : ShuntCompensatorType.REACTOR; + double oldSusceptancePerSection = model.getBPerSection(); + double oldQAtNominalV = Math.abs(Math.pow(voltageLevel.getNominalV(), 2) * oldSusceptancePerSection); + double oldMaxQAtNominalV = oldQAtNominalV * shuntCompensator.getMaximumSectionCount(); + double oldSwitchedOnSusceptance = oldSusceptancePerSection * shuntCompensator.getSectionCount(); + double oldSwitchedOnQAtNominalV = oldQAtNominalV * shuntCompensator.getSectionCount(); + + if (modificationInfos.getShuntCompensatorType() != null || modificationInfos.getMaxQAtNominalV() != null) { + modificationInfos.setMaxSusceptance(null); + } + + // due to cross validation between maximum section count and section count, we need to modify section count first + // when maximum section count old value is greater than the new one + if (modificationInfos.getMaximumSectionCount() != null && modificationInfos.getMaximumSectionCount().getValue() < shuntCompensator.getMaximumSectionCount()) { + modifySectionCount(modificationInfos.getSectionCount(), reports, shuntCompensator); + modifyMaximumSectionCount(modificationInfos.getMaximumSectionCount(), + modificationInfos.getMaxSusceptance(), + modificationInfos.getMaxQAtNominalV(), + reports, shuntCompensator, model); + } else { + modifyMaximumSectionCount(modificationInfos.getMaximumSectionCount(), + modificationInfos.getMaxSusceptance(), + modificationInfos.getMaxQAtNominalV(), + reports, shuntCompensator, model); + modifySectionCount(modificationInfos.getSectionCount(), reports, shuntCompensator); + } + + int maximumSectionCount = modificationInfos.getMaximumSectionCount() != null ? modificationInfos.getMaximumSectionCount().getValue() : shuntCompensator.getMaximumSectionCount(); + int sectionCount = modificationInfos.getSectionCount() != null ? modificationInfos.getSectionCount().getValue() : shuntCompensator.getSectionCount(); + + if (modificationInfos.getShuntCompensatorType() != null) { + reports.add(ModificationUtils.getInstance().buildModificationReport(shuntCompensatorType, modificationInfos.getShuntCompensatorType().getValue(), "Type")); + shuntCompensatorType = modificationInfos.getShuntCompensatorType().getValue(); + if (modificationInfos.getMaxQAtNominalV() == null) { + // we retrieve the absolute value of susceptance per section, then we determine the sign using the type + double bPerSectionAbsoluteValue = Math.abs(oldSusceptancePerSection); + double newBPerSection = shuntCompensatorType == ShuntCompensatorType.CAPACITOR ? bPerSectionAbsoluteValue : -bPerSectionAbsoluteValue; + model.setBPerSection(newBPerSection); + } + } + + if (modificationInfos.getMaxQAtNominalV() != null) { + modifyMaximumQAtNominalVoltage(modificationInfos.getMaxQAtNominalV(), voltageLevel, maximumSectionCount, reports, model, shuntCompensatorType); + } + if (modificationInfos.getMaxSusceptance() != null) { + modifyMaxSusceptance(modificationInfos.getMaxSusceptance(), maximumSectionCount, reports, model); + } + + reportSwitchedOnAndPerSectionValues(reports, oldQAtNominalV, oldSwitchedOnQAtNominalV, oldSusceptancePerSection, oldSwitchedOnSusceptance, oldMaxQAtNominalV, sectionCount, maximumSectionCount); + + reports.forEach(report -> insertReportNode(subReportNode, report)); + } + + public static void modifyMaxSusceptance(AttributeModification maxSusceptance, + int maximumSectionCount, + List reports, + ShuntCompensatorLinearModel model) { + double newSusceptancePerSection = maxSusceptance.getValue() / maximumSectionCount; + if (reports != null) { + double oldSusceptancePerSection = model.getBPerSection(); + double oldMaxSusceptance = oldSusceptancePerSection * maximumSectionCount; + reports.add(ModificationUtils.getInstance().buildModificationReport(oldMaxSusceptance, maxSusceptance.getValue(), "Maximal susceptance available")); + } + model.setBPerSection(newSusceptancePerSection); + } + + public static void modifyMaximumQAtNominalVoltage(AttributeModification maxQAtNominalV, + VoltageLevel voltageLevel, + int maximumSectionCount, + List reports, + ShuntCompensatorLinearModel model, + ShuntCompensatorType shuntCompensatorType) { + if (maxQAtNominalV.getValue() < 0) { + throw new NetworkModificationException(NetworkModificationException.Type.MODIFY_SHUNT_COMPENSATOR_ERROR, + "Qmax at nominal voltage should be greater or equal to 0"); + } + double newQatNominalV = maxQAtNominalV.getValue() / maximumSectionCount; + double newSusceptancePerSection = newQatNominalV / Math.pow(voltageLevel.getNominalV(), 2); + if (reports != null) { + double oldSusceptancePerSection = model.getBPerSection(); + double oldQAtNominalV = Math.abs(Math.pow(voltageLevel.getNominalV(), 2) * oldSusceptancePerSection); + double oldMaxQAtNominalV = oldQAtNominalV * maximumSectionCount; + reports.add(ModificationUtils.getInstance().buildModificationReport(oldMaxQAtNominalV, maxQAtNominalV.getValue(), "Qmax available at nominal voltage")); + } + + model.setBPerSection(shuntCompensatorType == ShuntCompensatorType.CAPACITOR ? newSusceptancePerSection : -newSusceptancePerSection); + } + + private void reportSwitchedOnAndPerSectionValues(List reports, double oldQAtNominalV, double oldSwitchedOnQAtNominalV, double oldSusceptancePerSection, double oldSwitchedOnSusceptance, double oldMaxQAtNominalV, int sectionCount, int maximumSectionCount) { + if (modificationInfos.getMaxQAtNominalV() != null) { + double newQatNominalV = modificationInfos.getMaxQAtNominalV().getValue() / maximumSectionCount; + double newSwitchedOnQAtNominalV = newQatNominalV * sectionCount; + reports.add(ModificationUtils.getInstance().buildModificationReport(oldQAtNominalV, newQatNominalV, "Q at nominal voltage per section")); + reports.add(ModificationUtils.getInstance().buildModificationReport(oldSwitchedOnQAtNominalV, newSwitchedOnQAtNominalV, SWITCHED_ON_Q_AT_NOMINALV_LOG_MESSAGE)); + } else if (modificationInfos.getMaxSusceptance() != null) { + double newSusceptancePerSection = modificationInfos.getMaxSusceptance().getValue() / maximumSectionCount; + double newSwitchedOnSusceptance = newSusceptancePerSection * sectionCount; + reports.add(ModificationUtils.getInstance().buildModificationReport(oldSusceptancePerSection, newSusceptancePerSection, "Susceptance per section")); + reports.add(ModificationUtils.getInstance().buildModificationReport(oldSwitchedOnSusceptance, newSwitchedOnSusceptance, "Switched-on susceptance")); + } else if (modificationInfos.getMaximumSectionCount() != null) { + double newQatNominalV = oldMaxQAtNominalV / maximumSectionCount; + double newSwitchedOnQAtNominalV = newQatNominalV * sectionCount; + reports.add(ModificationUtils.getInstance().buildModificationReport(oldQAtNominalV, newQatNominalV, "Q at nominal voltage per section")); + reports.add(ModificationUtils.getInstance().buildModificationReport(oldSwitchedOnQAtNominalV, newSwitchedOnQAtNominalV, SWITCHED_ON_Q_AT_NOMINALV_LOG_MESSAGE)); + } else if (modificationInfos.getSectionCount() != null) { + double newSwitchedOnQAtNominalV = oldQAtNominalV * sectionCount; + reports.add(ModificationUtils.getInstance().buildModificationReport(oldSwitchedOnQAtNominalV, newSwitchedOnQAtNominalV, SWITCHED_ON_Q_AT_NOMINALV_LOG_MESSAGE)); + } + } + + private ReportNode modifyShuntCompensatorConnectivityAttributes(ShuntCompensatorModificationInfos modificationInfos, + ShuntCompensator shuntCompensator, ReportNode subReportNode) { + ConnectablePosition connectablePosition = shuntCompensator.getExtension(ConnectablePosition.class); + ConnectablePositionAdder connectablePositionAdder = shuntCompensator.newExtension(ConnectablePositionAdder.class); + return ModificationUtils.getInstance().modifyInjectionConnectivityAttributes(connectablePosition, connectablePositionAdder, shuntCompensator, modificationInfos, subReportNode); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/StaticVarCompensatorCreation.java b/src/main/java/org/gridsuite/modification/modifications/StaticVarCompensatorCreation.java new file mode 100644 index 0000000..3933078 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/StaticVarCompensatorCreation.java @@ -0,0 +1,259 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.StandbyAutomatonAdder; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.StaticVarCompensatorCreationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static org.gridsuite.modification.NetworkModificationException.Type.STATIC_VAR_COMPENSATOR_ALREADY_EXISTS; +import static org.gridsuite.modification.utils.ModificationUtils.*; + +/** + * @author Ghazwa Rehili + */ +public class StaticVarCompensatorCreation extends AbstractModification { + + private final StaticVarCompensatorCreationInfos modificationInfos; + + public StaticVarCompensatorCreation(StaticVarCompensatorCreationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (network.getStaticVarCompensator(modificationInfos.getEquipmentId()) != null) { + throw new NetworkModificationException(STATIC_VAR_COMPENSATOR_ALREADY_EXISTS, modificationInfos.getEquipmentId()); + } + + // check connectivity + ModificationUtils.getInstance() + .controlConnectivity(network, modificationInfos.getVoltageLevelId(), + modificationInfos.getBusOrBusbarSectionId(), modificationInfos.getConnectionPosition()); + + // check reactive power limits and set points + ModificationUtils.getInstance().checkReactivePowerLimitsAndSetPointsCreation(modificationInfos); + + // check regulated terminal + VoltageLevel voltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId()); + ModificationUtils.getInstance().getTerminalFromIdentifiable(voltageLevel.getNetwork(), modificationInfos.getRegulatingTerminalId(), + modificationInfos.getRegulatingTerminalType(), modificationInfos.getRegulatingTerminalVlId()); + + // check standby automaton + ModificationUtils.getInstance().checkStandbyAutomatonCreation(modificationInfos); + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + // create the static var compensator in the network + VoltageLevel voltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId()); + if (voltageLevel.getTopologyKind() == TopologyKind.NODE_BREAKER) { + createStaticVarCompensatorInNodeBreaker(voltageLevel, modificationInfos, network, subReportNode); + } else { + createStaticVarCompensatorInBusBreaker(voltageLevel, modificationInfos, subReportNode); + } + ModificationUtils.getInstance().disconnectCreatedInjection(modificationInfos, network.getStaticVarCompensator(modificationInfos.getEquipmentId()), subReportNode); + // properties + StaticVarCompensator staticVarCompensator = network.getStaticVarCompensator(modificationInfos.getEquipmentId()); + PropertiesUtils.applyProperties(staticVarCompensator, subReportNode, modificationInfos.getProperties(), "StaticVarCompensatorProperties"); + subReportNode.newReportNode() + .withMessageTemplate("staticVarCompensatorCreated", "New static var compensator with id=${id} created") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + + private void createStaticVarCompensatorInNodeBreaker(VoltageLevel voltageLevel, StaticVarCompensatorCreationInfos staticVarCompensatorCreationInfos, + Network network, ReportNode subReportNode) { + StaticVarCompensatorAdder staticVarCompensatorAdder = createStaticVarCompensatorAdderInNodeBreaker(voltageLevel, staticVarCompensatorCreationInfos); + createInjectionInNodeBreaker(voltageLevel, staticVarCompensatorCreationInfos, network, staticVarCompensatorAdder, subReportNode); + var staticVarCompensator = ModificationUtils.getInstance().getStaticVarCompensator(network, staticVarCompensatorCreationInfos.getEquipmentId()); + addExtensionsToStaticVarCompensator(staticVarCompensatorCreationInfos, staticVarCompensator, voltageLevel, subReportNode); + } + + private StaticVarCompensatorAdder createStaticVarCompensatorAdderInNodeBreaker(VoltageLevel voltageLevel, StaticVarCompensatorCreationInfos staticVarCompensatorCreationInfos) { + Terminal terminal = ModificationUtils.getInstance().getTerminalFromIdentifiable(voltageLevel.getNetwork(), + staticVarCompensatorCreationInfos.getRegulatingTerminalId(), + staticVarCompensatorCreationInfos.getRegulatingTerminalType(), + staticVarCompensatorCreationInfos.getRegulatingTerminalVlId()); + double bMax = Objects.isNull(staticVarCompensatorCreationInfos.getMaxSusceptance()) && Objects.nonNull(staticVarCompensatorCreationInfos.getMaxQAtNominalV()) ? + (staticVarCompensatorCreationInfos.getMaxQAtNominalV()) / Math.pow(voltageLevel.getNominalV(), 2) : staticVarCompensatorCreationInfos.getMaxSusceptance(); + double bMin = Objects.isNull(staticVarCompensatorCreationInfos.getMinSusceptance()) && Objects.nonNull(staticVarCompensatorCreationInfos.getMinQAtNominalV()) ? + (staticVarCompensatorCreationInfos.getMinQAtNominalV()) / Math.pow(voltageLevel.getNominalV(), 2) : staticVarCompensatorCreationInfos.getMinSusceptance(); + StaticVarCompensatorAdder staticVarCompensatorAdder = voltageLevel.newStaticVarCompensator() + .setId(staticVarCompensatorCreationInfos.getEquipmentId()) + .setName(staticVarCompensatorCreationInfos.getEquipmentName()) + .setBmax(bMax) + .setBmin(bMin) + .setVoltageSetpoint(nanIfNull(staticVarCompensatorCreationInfos.getVoltageSetpoint())) + .setReactivePowerSetpoint(nanIfNull(staticVarCompensatorCreationInfos.getReactivePowerSetpoint())) + .setRegulationMode(staticVarCompensatorCreationInfos.getRegulationMode()); + + if (terminal != null) { + staticVarCompensatorAdder.setRegulatingTerminal(terminal); + } + + return staticVarCompensatorAdder; + } + + private void addExtensionsToStaticVarCompensator(StaticVarCompensatorCreationInfos staticVarCompensatorCreationInfos, + StaticVarCompensator staticVarCompensator, + VoltageLevel voltageLevel, + ReportNode subReportNode) { + if (staticVarCompensatorCreationInfos.getEquipmentName() != null) { + ModificationUtils.getInstance().reportElementaryCreation(subReportNode, staticVarCompensatorCreationInfos.getEquipmentName(), "Name"); + } + + reportInjectionCreationConnectivity(staticVarCompensatorCreationInfos, subReportNode); + reportStaticVarCompensatorLimitsAndSetpoints(staticVarCompensatorCreationInfos, staticVarCompensator, voltageLevel, subReportNode); + reportStaticVarCompensatorStandbyAutomaton(staticVarCompensatorCreationInfos, staticVarCompensator, voltageLevel, subReportNode); + } + + private void reportStaticVarCompensatorStandbyAutomaton(StaticVarCompensatorCreationInfos staticVarCompensatorCreationInfos, + StaticVarCompensator staticVarCompensator, VoltageLevel voltageLevel, ReportNode subReportNode) { + if (Boolean.TRUE.equals(staticVarCompensatorCreationInfos.isStandbyAutomatonOn())) { + List standbyAutomatonReports = new ArrayList<>(); + double b0 = Objects.isNull(staticVarCompensatorCreationInfos.getB0()) && Objects.nonNull(staticVarCompensatorCreationInfos.getQ0()) ? + (staticVarCompensatorCreationInfos.getQ0()) / Math.pow(voltageLevel.getNominalV(), 2) : staticVarCompensatorCreationInfos.getB0(); + try { + staticVarCompensator.newExtension(StandbyAutomatonAdder.class) + .withStandbyStatus(staticVarCompensatorCreationInfos.isStandby()) + .withB0(b0) + .withLowVoltageSetpoint(staticVarCompensatorCreationInfos.getLowVoltageSetpoint()) + .withHighVoltageSetpoint(staticVarCompensatorCreationInfos.getHighVoltageSetpoint()) + .withLowVoltageThreshold(staticVarCompensatorCreationInfos.getLowVoltageThreshold()) + .withHighVoltageThreshold(staticVarCompensatorCreationInfos.getHighVoltageThreshold()) + .add(); + standbyAutomatonReports.add(ModificationUtils.getInstance().buildCreationReport( + staticVarCompensatorCreationInfos.isStandby(), + "Standby")); + if (Objects.nonNull(staticVarCompensatorCreationInfos.getB0())) { + standbyAutomatonReports.add(ModificationUtils.getInstance().buildCreationReport( + staticVarCompensatorCreationInfos.getB0(), + "Fixed part of susceptance")); + } + if (Objects.nonNull(staticVarCompensatorCreationInfos.getQ0())) { + standbyAutomatonReports.add(ModificationUtils.getInstance().buildCreationReport( + staticVarCompensatorCreationInfos.getQ0(), + "Fixed part of Q at nominal voltage")); + } + standbyAutomatonReports.add(ModificationUtils.getInstance().buildCreationReport( + staticVarCompensatorCreationInfos.getLowVoltageSetpoint(), + "Low voltage set point")); + standbyAutomatonReports.add(ModificationUtils.getInstance().buildCreationReport( + staticVarCompensatorCreationInfos.getHighVoltageSetpoint(), + "High voltage set point")); + standbyAutomatonReports.add(ModificationUtils.getInstance().buildCreationReport( + staticVarCompensatorCreationInfos.getHighVoltageThreshold(), + "High voltage threshold")); + standbyAutomatonReports.add(ModificationUtils.getInstance().buildCreationReport( + staticVarCompensatorCreationInfos.getLowVoltageThreshold(), + "Low voltage threshold")); + } catch (PowsyblException e) { + standbyAutomatonReports.add(ReportNode.newRootReportNode() + .withMessageTemplate("StandbyAutomatonExtensionAddError", + "Cannot add standby automaton extension on ${message}") + .withUntypedValue("message", e.getMessage()) + .withSeverity(TypedValue.ERROR_SEVERITY) + .build()); + } + ModificationUtils.getInstance().reportModifications(subReportNode, standbyAutomatonReports, + "StandbyAutomatonCreated", "Standby automaton"); + } + } + + private void createStaticVarCompensatorInBusBreaker(VoltageLevel voltageLevel, StaticVarCompensatorCreationInfos staticVarCompensatorCreationInfos, + ReportNode subReportNode) { + + Bus bus = ModificationUtils.getInstance().getBusBreakerBus(voltageLevel, staticVarCompensatorCreationInfos.getBusOrBusbarSectionId()); + double bMax = Objects.isNull(staticVarCompensatorCreationInfos.getMaxSusceptance()) && Objects.nonNull(staticVarCompensatorCreationInfos.getMaxQAtNominalV()) ? + (staticVarCompensatorCreationInfos.getMaxQAtNominalV()) / Math.pow(voltageLevel.getNominalV(), 2) : staticVarCompensatorCreationInfos.getMaxSusceptance(); + double bMin = Objects.isNull(staticVarCompensatorCreationInfos.getMinSusceptance()) && Objects.nonNull(staticVarCompensatorCreationInfos.getMinQAtNominalV()) ? + (staticVarCompensatorCreationInfos.getMinQAtNominalV()) / Math.pow(voltageLevel.getNominalV(), 2) : staticVarCompensatorCreationInfos.getMinSusceptance(); + /* creating the static var compensator */ + StaticVarCompensator staticVarCompensator = voltageLevel.newStaticVarCompensator() + .setId(staticVarCompensatorCreationInfos.getEquipmentId()) + .setName(staticVarCompensatorCreationInfos.getEquipmentName()) + .setBus(bus.getId()) + .setConnectableBus(bus.getId()) + .setBmax(bMax) + .setBmin(bMin) + .setVoltageSetpoint(staticVarCompensatorCreationInfos.getVoltageSetpoint()) + .setReactivePowerSetpoint(staticVarCompensatorCreationInfos.getReactivePowerSetpoint()) + .setRegulationMode(staticVarCompensatorCreationInfos.getRegulationMode()) + .add(); + + addExtensionsToStaticVarCompensator(staticVarCompensatorCreationInfos, staticVarCompensator, voltageLevel, subReportNode); + } + + private void reportStaticVarCompensatorLimitsAndSetpoints(StaticVarCompensatorCreationInfos staticVarCompensatorCreationInfos, + StaticVarCompensator staticVarCompensator, VoltageLevel voltageLevel, ReportNode subReportNode) { + List voltageReports = new ArrayList<>(); + if (Objects.nonNull(staticVarCompensatorCreationInfos.getMinSusceptance())) { + voltageReports.add(ModificationUtils.getInstance().buildCreationReport(staticVarCompensatorCreationInfos.getMinSusceptance(), "Susceptance min")); + } + if (Objects.nonNull(staticVarCompensatorCreationInfos.getMaxSusceptance())) { + voltageReports.add(ModificationUtils.getInstance().buildCreationReport(staticVarCompensatorCreationInfos.getMaxSusceptance(), "Susceptance max")); + } + if (Objects.nonNull(staticVarCompensatorCreationInfos.getMinQAtNominalV())) { + voltageReports.add(ModificationUtils.getInstance().buildCreationReport(staticVarCompensatorCreationInfos.getMinQAtNominalV(), "Q min at nominal voltage")); + } + if (Objects.nonNull(staticVarCompensatorCreationInfos.getMaxQAtNominalV())) { + voltageReports.add(ModificationUtils.getInstance().buildCreationReport(staticVarCompensatorCreationInfos.getMaxQAtNominalV(), "Q max at nominal voltage")); + } + if (Objects.nonNull(staticVarCompensatorCreationInfos.getRegulationMode())) { + voltageReports.add(ModificationUtils.getInstance().buildCreationReport(staticVarCompensatorCreationInfos.getRegulationMode(), "regulation mode")); + } + if (Objects.nonNull(staticVarCompensatorCreationInfos.getVoltageSetpoint())) { + voltageReports.add(ModificationUtils.getInstance().buildCreationReport(staticVarCompensatorCreationInfos.getVoltageSetpoint(), "Voltage set point")); + } + if (Objects.nonNull(staticVarCompensatorCreationInfos.getReactivePowerSetpoint())) { + voltageReports.add(ModificationUtils.getInstance().buildCreationReport(staticVarCompensatorCreationInfos.getReactivePowerSetpoint(), "Reactive power set point")); + } + if (Objects.nonNull(staticVarCompensatorCreationInfos.getVoltageRegulationType())) { + voltageReports.add(ModificationUtils.getInstance().buildCreationReport(staticVarCompensatorCreationInfos.getVoltageRegulationType(), "Voltage Regulation type")); + } + if (staticVarCompensatorCreationInfos.getRegulatingTerminalVlId() != null && staticVarCompensatorCreationInfos.getRegulatingTerminalId() != null && + staticVarCompensatorCreationInfos.getRegulatingTerminalType() != null) { + Terminal terminal = ModificationUtils.getInstance().getTerminalFromIdentifiable(voltageLevel.getNetwork(), + staticVarCompensatorCreationInfos.getRegulatingTerminalId(), + staticVarCompensatorCreationInfos.getRegulatingTerminalType(), + staticVarCompensatorCreationInfos.getRegulatingTerminalVlId()); + if (terminal != null) { + updateCompensatorRegulatingTerminal(staticVarCompensatorCreationInfos, staticVarCompensator, terminal, voltageReports); + } + } + ModificationUtils.getInstance().reportModifications(subReportNode, voltageReports, "LimitsAndSetpointsCreated", "Limits and Setpoints"); + } + + private void updateCompensatorRegulatingTerminal(StaticVarCompensatorCreationInfos staticVarCompensatorCreationInfos, StaticVarCompensator staticVarCompensator, + Terminal terminal, List voltageReports) { + if (staticVarCompensatorCreationInfos.getRegulatingTerminalId() != null + && staticVarCompensatorCreationInfos.getRegulatingTerminalType() != null + && staticVarCompensatorCreationInfos.getRegulatingTerminalVlId() != null) { + staticVarCompensator.setRegulatingTerminal(terminal); + voltageReports.add(ModificationUtils.getInstance().buildCreationReport( + staticVarCompensatorCreationInfos.getRegulatingTerminalVlId(), + "Voltage level")); + voltageReports.add(ModificationUtils.getInstance().buildCreationReport( + staticVarCompensatorCreationInfos.getRegulatingTerminalType() + ":" + + staticVarCompensatorCreationInfos.getRegulatingTerminalId(), + "Equipment")); + } + } + +} diff --git a/src/main/java/org/gridsuite/modification/modifications/SubstationCreation.java b/src/main/java/org/gridsuite/modification/modifications/SubstationCreation.java new file mode 100644 index 0000000..efec57e --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/SubstationCreation.java @@ -0,0 +1,46 @@ +package org.gridsuite.modification.modifications; + +import org.gridsuite.modification.dto.SubstationCreationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.Substation; + +public class SubstationCreation extends AbstractModification { + + private final SubstationCreationInfos modificationInfos; + + public SubstationCreation(SubstationCreationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + Substation substation = network.newSubstation() + .setId(modificationInfos.getEquipmentId()) + .setName(modificationInfos.getEquipmentName()) + .setCountry(modificationInfos.getCountry()) + .add(); + + subReportNode.newReportNode() + .withMessageTemplate("substationCreated", "New substation with id=${id} created") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + + // name and country + if (modificationInfos.getEquipmentName() != null) { + ModificationUtils.getInstance() + .reportElementaryCreation(subReportNode, modificationInfos.getEquipmentName(), "Name"); + } + if (modificationInfos.getCountry() != null) { + ModificationUtils.getInstance() + .reportElementaryCreation(subReportNode, modificationInfos.getCountry(), "Country"); + } + // properties + PropertiesUtils.applyProperties(substation, subReportNode, modificationInfos.getProperties(), "SubstationProperties"); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/SubstationModification.java b/src/main/java/org/gridsuite/modification/modifications/SubstationModification.java new file mode 100644 index 0000000..e385fff --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/SubstationModification.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.Substation; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.SubstationModificationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import static org.gridsuite.modification.NetworkModificationException.Type.SUBSTATION_NOT_FOUND; + +/* + * @author David Braquart + */ +public class SubstationModification extends AbstractModification { + + private final SubstationModificationInfos modificationInfos; + + public SubstationModification(SubstationModificationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + Substation station = network.getSubstation(modificationInfos.getEquipmentId()); + if (station == null) { + throw new NetworkModificationException(SUBSTATION_NOT_FOUND, + "Substation " + modificationInfos.getEquipmentId() + " does not exist in network"); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + Substation station = network.getSubstation(modificationInfos.getEquipmentId()); + + // modify the substation in the network + subReportNode.newReportNode() + .withMessageTemplate("substationModification", "Substation with id=${id} modified :") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + // name and country + ModificationUtils.getInstance().applyElementaryModifications(station::setName, () -> station.getOptionalName().orElse("No value"), modificationInfos.getEquipmentName(), subReportNode, "Name"); + ModificationUtils.getInstance().applyElementaryModifications(station::setCountry, station::getNullableCountry, modificationInfos.getCountry(), subReportNode, "Country"); + // properties + PropertiesUtils.applyProperties(station, subReportNode, modificationInfos.getProperties(), "SubstationProperties"); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/TabularCreation.java b/src/main/java/org/gridsuite/modification/modifications/TabularCreation.java new file mode 100644 index 0000000..f91b6e1 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/TabularCreation.java @@ -0,0 +1,88 @@ +/* + Copyright (c) 2024, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.Network; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.TabularCreationInfos; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.gridsuite.modification.NetworkModificationException.Type.TABULAR_CREATION_ERROR; + +/** + * @author Franck Lecuyer + */ +public class TabularCreation extends AbstractModification { + + private static final Logger LOGGER = LoggerFactory.getLogger(TabularCreation.class); + + private static final String TABULAR_CREATION_REPORT_KEY_PREFIX = "tabular"; + + private final TabularCreationInfos creationInfos; + + public TabularCreation(TabularCreationInfos creationInfos) { + this.creationInfos = creationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (creationInfos == null) { + throw new NetworkModificationException(TABULAR_CREATION_ERROR, "No tabular creation to apply !!"); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + int applicationFailuresCount = 0; + for (var creatInfos : creationInfos.getCreations()) { + try { + AbstractModification modification = creatInfos.toModification(); + modification.check(network); + modification.apply(network); + } catch (PowsyblException e) { + applicationFailuresCount++; + subReportNode.newReportNode() + .withMessageTemplate(creatInfos.getType().name() + applicationFailuresCount, "${message}") + .withUntypedValue("message", e.getMessage()) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + LOGGER.warn(e.getMessage()); + } + } + String defaultMessage = switch (creationInfos.getCreationType()) { + case GENERATOR_CREATION -> "generators"; + default -> "equipments of unknown type"; + } + " have been created"; + + if (creationInfos.getCreations().size() == applicationFailuresCount) { + subReportNode.newReportNode() + .withMessageTemplate(TABULAR_CREATION_REPORT_KEY_PREFIX + creationInfos.getCreationType().name() + "Error", "Tabular creation: No ${defaultMessage}") + .withUntypedValue("defaultMessage", defaultMessage) + .withSeverity(TypedValue.ERROR_SEVERITY) + .add(); + } else if (applicationFailuresCount > 0) { + subReportNode.newReportNode() + .withMessageTemplate(TABULAR_CREATION_REPORT_KEY_PREFIX + creationInfos.getCreationType().name() + "Warning", "Tabular creation: ${creationsCount} ${defaultMessage} and ${failuresCount} have not been created") + .withUntypedValue("creationsCount", creationInfos.getCreations().size() - applicationFailuresCount) + .withUntypedValue("failuresCount", applicationFailuresCount) + .withUntypedValue("defaultMessage", defaultMessage) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + } else { + subReportNode.newReportNode() + .withMessageTemplate(TABULAR_CREATION_REPORT_KEY_PREFIX + creationInfos.getCreationType().name(), "Tabular creation: ${creationsCount} ${defaultMessage}") + .withUntypedValue("creationsCount", creationInfos.getCreations().size()) + .withUntypedValue("defaultMessage", defaultMessage) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/TabularModification.java b/src/main/java/org/gridsuite/modification/modifications/TabularModification.java new file mode 100644 index 0000000..846c27e --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/TabularModification.java @@ -0,0 +1,135 @@ +/* + Copyright (c) 2023, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ShuntCompensatorModelType; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.ShuntCompensatorModificationInfos; +import org.gridsuite.modification.dto.TabularModificationInfos; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.gridsuite.modification.NetworkModificationException.Type.TABULAR_MODIFICATION_ERROR; + +/** + * @author Etienne Homer + */ +public class TabularModification extends AbstractModification { + + private static final Logger LOGGER = LoggerFactory.getLogger(TabularModification.class); + + private static final String TABULAR_MODIFICATION_REPORT_KEY_PREFIX = "tabular"; + + private final TabularModificationInfos modificationInfos; + + public TabularModification(TabularModificationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (modificationInfos == null) { + throw new NetworkModificationException(TABULAR_MODIFICATION_ERROR, "No tabular modification to apply !!"); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + int applicationFailuresCount = 0; + for (var modifInfos : modificationInfos.getModifications()) { + try { + AbstractModification modification = modifInfos.toModification(); + modification.check(network); + if (modifInfos instanceof ShuntCompensatorModificationInfos shuntModification) { + checkShuntCompensatorModification(network, shuntModification, subReportNode); + } + modification.apply(network); + } catch (PowsyblException e) { + applicationFailuresCount++; + subReportNode.newReportNode() + .withMessageTemplate(modifInfos.getType().name() + applicationFailuresCount, "${message}") + .withUntypedValue("message", e.getMessage()) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + LOGGER.warn(e.getMessage()); + } + } + String defaultMessage = switch (modificationInfos.getModificationType()) { + case GENERATOR_MODIFICATION -> "generators"; + case LOAD_MODIFICATION -> "loads"; + case TWO_WINDINGS_TRANSFORMER_MODIFICATION -> "two windings transformers"; + case BATTERY_MODIFICATION -> "batteries"; + case VOLTAGE_LEVEL_MODIFICATION -> "voltage levels"; + case SHUNT_COMPENSATOR_MODIFICATION -> "shunt compensators"; + case LINE_MODIFICATION -> "lines"; + case SUBSTATION_MODIFICATION -> "substations"; + default -> "equipments of unknown type"; + } + " have been modified"; + + if (modificationInfos.getModifications().size() == applicationFailuresCount) { + subReportNode.newReportNode() + .withMessageTemplate(TABULAR_MODIFICATION_REPORT_KEY_PREFIX + modificationInfos.getModificationType().name() + "Error", "Tabular modification: No ${defaultMessage}") + .withUntypedValue("defaultMessage", defaultMessage) + .withSeverity(TypedValue.ERROR_SEVERITY) + .add(); + } else if (applicationFailuresCount > 0) { + subReportNode.newReportNode() + .withMessageTemplate(TABULAR_MODIFICATION_REPORT_KEY_PREFIX + modificationInfos.getModificationType().name() + "Warning", "Tabular modification: ${modificationsCount} ${defaultMessage} and ${failuresCount} have not been modified") + .withUntypedValue("modificationsCount", modificationInfos.getModifications().size() - applicationFailuresCount) + .withUntypedValue("failuresCount", applicationFailuresCount) + .withUntypedValue("defaultMessage", defaultMessage) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + } else { + subReportNode.newReportNode() + .withMessageTemplate(TABULAR_MODIFICATION_REPORT_KEY_PREFIX + modificationInfos.getModificationType().name(), "Tabular modification: ${modificationsCount} ${defaultMessage}") + .withUntypedValue("modificationsCount", modificationInfos.getModifications().size()) + .withUntypedValue("defaultMessage", defaultMessage) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + } + + public void checkShuntCompensatorModification( + Network network, + ShuntCompensatorModificationInfos shuntCompensatorModificationInfos, + ReportNode subReportNode + ) { + var shuntCompensator = network.getShuntCompensator(shuntCompensatorModificationInfos.getEquipmentId()); + if (shuntCompensator.getModelType() == ShuntCompensatorModelType.NON_LINEAR) { + subReportNode.newReportNode() + .withMessageTemplate(shuntCompensator.getId(), "Tabular modification: It is currently not possible to modify non-linear shunt compensator with id ${id}") + .withUntypedValue("id", shuntCompensator.getId()) + .withSeverity(TypedValue.ERROR_SEVERITY) + .add(); + } else if (shuntCompensatorModificationInfos.getMaxSusceptance() != null) { + if (shuntCompensatorModificationInfos.getShuntCompensatorType() != null && shuntCompensatorModificationInfos.getMaxQAtNominalV() != null) { + subReportNode.newReportNode() + .withMessageTemplate(shuntCompensator.getId(), "Tabular modification: Input for maximum susceptance has been ignored since it is not possible to simultaneously update type, maximum reactive power and maximum susceptance for shunt compensator with id ${id}") + .withUntypedValue("id", shuntCompensator.getId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + } else if (shuntCompensatorModificationInfos.getShuntCompensatorType() != null) { + subReportNode.newReportNode() + .withMessageTemplate(shuntCompensator.getId(), "Tabular modification: Input for maximum susceptance has been ignored since it is not possible to simultaneously update type and maximum susceptance for shunt compensator with id ${id}") + .withUntypedValue("id", shuntCompensator.getId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + } else if (shuntCompensatorModificationInfos.getMaxQAtNominalV() != null) { + subReportNode.newReportNode() + .withMessageTemplate(shuntCompensator.getId(), "Tabular modification: Input for maximum susceptance has been ignored since it is not possible to simultaneously update maximum reactive power and maximum susceptance for shunt compensator with id ${id}") + .withUntypedValue("id", shuntCompensator.getId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + } + } + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/TwoWindingsTransformerCreation.java b/src/main/java/org/gridsuite/modification/modifications/TwoWindingsTransformerCreation.java new file mode 100644 index 0000000..0ec3194 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/TwoWindingsTransformerCreation.java @@ -0,0 +1,209 @@ +/* + Copyright (c) 2023, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.modification.topology.CreateBranchFeederBays; +import com.powsybl.iidm.modification.topology.CreateBranchFeederBaysBuilder; +import com.powsybl.iidm.network.*; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.*; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import java.util.Optional; + +import static org.gridsuite.modification.NetworkModificationException.Type.*; + +public class TwoWindingsTransformerCreation extends AbstractModification { + + private final TwoWindingsTransformerCreationInfos modificationInfos; + + public TwoWindingsTransformerCreation(TwoWindingsTransformerCreationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (network.getTwoWindingsTransformer(modificationInfos.getEquipmentId()) != null) { + throw new NetworkModificationException(TWO_WINDINGS_TRANSFORMER_ALREADY_EXISTS, modificationInfos.getEquipmentId()); + } + ModificationUtils.getInstance().controlBranchCreation(network, + modificationInfos.getVoltageLevelId1(), modificationInfos.getBusOrBusbarSectionId1(), modificationInfos.getConnectionPosition1(), + modificationInfos.getVoltageLevelId2(), modificationInfos.getBusOrBusbarSectionId2(), modificationInfos.getConnectionPosition2()); + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + // create the 2wt in the network + VoltageLevel voltageLevel1 = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId1()); + VoltageLevel voltageLevel2 = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getVoltageLevelId2()); + TwoWindingsTransformer twoWindingsTransformer; + + if (voltageLevel1.getTopologyKind() == TopologyKind.NODE_BREAKER && voltageLevel2.getTopologyKind() == TopologyKind.NODE_BREAKER) { + twoWindingsTransformer = create2WTInNodeBreaker(network, voltageLevel1, voltageLevel2, subReportNode); + } else { + // Create 2wt in bus/mixed breaker + twoWindingsTransformer = create2WTInOtherBreaker(network, voltageLevel1, voltageLevel2, modificationInfos, true, true, subReportNode); + } + + // Set permanent and temporary current limits + CurrentLimitsInfos currentLimitsInfos1 = modificationInfos.getCurrentLimits1(); + CurrentLimitsInfos currentLimitsInfos2 = modificationInfos.getCurrentLimits2(); + if (currentLimitsInfos1 != null || currentLimitsInfos2 != null) { + ModificationUtils.getInstance().setCurrentLimits(currentLimitsInfos1, twoWindingsTransformer.newCurrentLimits1()); + ModificationUtils.getInstance().setCurrentLimits(currentLimitsInfos2, twoWindingsTransformer.newCurrentLimits2()); + } + + ModificationUtils.getInstance().disconnectBranch(modificationInfos, network.getTwoWindingsTransformer(modificationInfos.getEquipmentId()), subReportNode); + // properties + PropertiesUtils.applyProperties(twoWindingsTransformer, subReportNode, modificationInfos.getProperties(), "TwoWindingsTransformerProperties"); + } + + private TwoWindingsTransformer create2WTInNodeBreaker(Network network, VoltageLevel voltageLevel1, VoltageLevel voltageLevel2, ReportNode subReportNode) { + var twoWindingsTransformerAdder = createTwoWindingsTransformerAdder(voltageLevel1, voltageLevel2, modificationInfos, false, false); + + var position1 = ModificationUtils.getInstance().getPosition(modificationInfos.getConnectionPosition1(), modificationInfos.getBusOrBusbarSectionId1(), network, voltageLevel1); + var position2 = ModificationUtils.getInstance().getPosition(modificationInfos.getConnectionPosition2(), modificationInfos.getBusOrBusbarSectionId2(), network, voltageLevel2); + + CreateBranchFeederBays algo = new CreateBranchFeederBaysBuilder() + .withBusOrBusbarSectionId1(modificationInfos.getBusOrBusbarSectionId1()) + .withBusOrBusbarSectionId2(modificationInfos.getBusOrBusbarSectionId2()) + .withFeederName1(modificationInfos.getConnectionName1() != null ? modificationInfos.getConnectionName1() : modificationInfos.getEquipmentId()) + .withFeederName2(modificationInfos.getConnectionName2() != null ? modificationInfos.getConnectionName2() : modificationInfos.getEquipmentId()) + .withDirection1(modificationInfos.getConnectionDirection1()) + .withDirection2(modificationInfos.getConnectionDirection2()) + .withPositionOrder1(position1) + .withPositionOrder2(position2) + .withBranchAdder(twoWindingsTransformerAdder).build(); + algo.apply(network, true, subReportNode); + + var twt = network.getTwoWindingsTransformer(modificationInfos.getEquipmentId()); + addTapChangersToTwoWindingsTransformer(network, modificationInfos, twt); + + return twt; + } + + private TwoWindingsTransformerAdder createTwoWindingsTransformerAdder(VoltageLevel voltageLevel1, VoltageLevel voltageLevel2, TwoWindingsTransformerCreationInfos twoWindingsTransformerCreationInfos, boolean withSwitch1, boolean withSwitch2) { + Optional optS1 = voltageLevel1.getSubstation(); + Optional optS2 = voltageLevel2.getSubstation(); + Substation s1 = optS1.orElse(null); + Substation s2 = optS2.orElse(null); + BranchAdder branchAdder; + + if (s1 != null) { + branchAdder = s1.newTwoWindingsTransformer(); + } else if (s2 != null) { + branchAdder = s2.newTwoWindingsTransformer(); + } else { + throw new NetworkModificationException(TWO_WINDINGS_TRANSFORMER_CREATION_ERROR, "The two windings transformer should belong to a substation"); + } + // common settings + TwoWindingsTransformerAdder twoWindingsTransformerAdder = branchAdder.setId(twoWindingsTransformerCreationInfos.getEquipmentId()) + .setName(twoWindingsTransformerCreationInfos.getEquipmentName()) + .setVoltageLevel1(twoWindingsTransformerCreationInfos.getVoltageLevelId1()) + .setVoltageLevel2(twoWindingsTransformerCreationInfos.getVoltageLevelId2()) + .setG(twoWindingsTransformerCreationInfos.getG()) + .setB(twoWindingsTransformerCreationInfos.getB()) + .setR(twoWindingsTransformerCreationInfos.getR()) + .setX(twoWindingsTransformerCreationInfos.getX()) + .setRatedU1(twoWindingsTransformerCreationInfos.getRatedU1()) + .setRatedU2(twoWindingsTransformerCreationInfos.getRatedU2()); + + if (twoWindingsTransformerCreationInfos.getRatedS() != null) { + twoWindingsTransformerAdder.setRatedS(twoWindingsTransformerCreationInfos.getRatedS()); + } + + // BranchAdder completion by topology + ModificationUtils.getInstance().setBranchAdderNodeOrBus(branchAdder, voltageLevel1, twoWindingsTransformerCreationInfos, TwoSides.ONE, withSwitch1); + ModificationUtils.getInstance().setBranchAdderNodeOrBus(branchAdder, voltageLevel2, twoWindingsTransformerCreationInfos, TwoSides.TWO, withSwitch2); + + return twoWindingsTransformerAdder; + } + + private void addTapChangersToTwoWindingsTransformer(Network network, TwoWindingsTransformerCreationInfos twoWindingsTransformerCreationInfos, com.powsybl.iidm.network.TwoWindingsTransformer twt) { + if (twoWindingsTransformerCreationInfos.getRatioTapChanger() != null) { + addRatioTapChangersToTwoWindingsTransformer(network, twoWindingsTransformerCreationInfos, twt); + } + + if (twoWindingsTransformerCreationInfos.getPhaseTapChanger() != null) { + addPhaseTapChangersToTwoWindingsTransformer(network, twoWindingsTransformerCreationInfos, twt); + } + } + + private void addPhaseTapChangersToTwoWindingsTransformer(Network network, TwoWindingsTransformerCreationInfos twoWindingsTransformerCreationInfos, TwoWindingsTransformer twt) { + PhaseTapChangerCreationInfos phaseTapChangerInfos = twoWindingsTransformerCreationInfos.getPhaseTapChanger(); + PhaseTapChangerAdder phaseTapChangerAdder = twt.newPhaseTapChanger(); + Terminal terminal = ModificationUtils.getInstance().getTerminalFromIdentifiable(network, + phaseTapChangerInfos.getRegulatingTerminalId(), + phaseTapChangerInfos.getRegulatingTerminalType(), + phaseTapChangerInfos.getRegulatingTerminalVlId()); + phaseTapChangerAdder.setRegulationTerminal(terminal); + + if (phaseTapChangerInfos.isRegulating()) { + phaseTapChangerAdder.setRegulationValue(phaseTapChangerInfos.getRegulationValue()) + .setTargetDeadband(phaseTapChangerInfos.getTargetDeadband() != null ? phaseTapChangerInfos.getTargetDeadband() : 0.); + } + + phaseTapChangerAdder.setRegulating(phaseTapChangerInfos.isRegulating()) + .setRegulationMode(phaseTapChangerInfos.getRegulationMode()) + .setLowTapPosition(phaseTapChangerInfos.getLowTapPosition()) + .setTapPosition(phaseTapChangerInfos.getTapPosition()); + + if (phaseTapChangerInfos.getSteps() != null) { + for (TapChangerStepCreationInfos step : phaseTapChangerInfos.getSteps()) { + phaseTapChangerAdder.beginStep().setR(step.getR()).setX(step.getX()).setG(step.getG()).setB(step.getB()).setRho(step.getRho()).setAlpha(step.getAlpha()).endStep(); + } + + phaseTapChangerAdder.add(); + } + } + + private void addRatioTapChangersToTwoWindingsTransformer(Network network, TwoWindingsTransformerCreationInfos twoWindingsTransformerCreationInfos, TwoWindingsTransformer twt) { + RatioTapChangerCreationInfos ratioTapChangerInfos = twoWindingsTransformerCreationInfos.getRatioTapChanger(); + RatioTapChangerAdder ratioTapChangerAdder = twt.newRatioTapChanger(); + Terminal terminal = ModificationUtils.getInstance().getTerminalFromIdentifiable(network, + ratioTapChangerInfos.getRegulatingTerminalId(), + ratioTapChangerInfos.getRegulatingTerminalType(), + ratioTapChangerInfos.getRegulatingTerminalVlId()); + + Double targetDeadband = ratioTapChangerInfos.getTargetDeadband(); + if (targetDeadband == null) { + targetDeadband = ratioTapChangerInfos.isRegulating() ? 0. : Double.NaN; + } + ratioTapChangerAdder.setTargetV(ratioTapChangerInfos.getTargetV() != null ? ratioTapChangerInfos.getTargetV() : Double.NaN) + .setTargetDeadband(targetDeadband) + .setRegulationTerminal(terminal); + + ratioTapChangerAdder.setRegulating(ratioTapChangerInfos.isRegulating()) + .setLoadTapChangingCapabilities(ratioTapChangerInfos.isLoadTapChangingCapabilities()) + .setLowTapPosition(ratioTapChangerInfos.getLowTapPosition()) + .setTapPosition(ratioTapChangerInfos.getTapPosition()); + + if (ratioTapChangerInfos.getSteps() != null) { + for (TapChangerStepCreationInfos step : ratioTapChangerInfos.getSteps()) { + ratioTapChangerAdder.beginStep().setR(step.getR()).setX(step.getX()).setG(step.getG()).setB(step.getB()).setRho(step.getRho()).endStep(); + } + + ratioTapChangerAdder.add(); + } + } + + private TwoWindingsTransformer create2WTInOtherBreaker(Network network, VoltageLevel voltageLevel1, VoltageLevel voltageLevel2, TwoWindingsTransformerCreationInfos twoWindingsTransformerCreationInfos, boolean withSwitch1, boolean withSwitch2, ReportNode subReportNode) { + var twt = createTwoWindingsTransformerAdder(voltageLevel1, voltageLevel2, twoWindingsTransformerCreationInfos, withSwitch1, withSwitch2).add(); + addTapChangersToTwoWindingsTransformer(network, twoWindingsTransformerCreationInfos, twt); + subReportNode.newReportNode() + .withMessageTemplate("twoWindingsTransformerCreated", "New two windings transformer with id=${id} created") + .withUntypedValue("id", twoWindingsTransformerCreationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + + return twt; + } + +} diff --git a/src/main/java/org/gridsuite/modification/modifications/TwoWindingsTransformerModification.java b/src/main/java/org/gridsuite/modification/modifications/TwoWindingsTransformerModification.java new file mode 100644 index 0000000..c0a89e4 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/TwoWindingsTransformerModification.java @@ -0,0 +1,688 @@ +/* + Copyright (c) 2023, RTE (http://www.rte-france.com) + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.TapChangerType; +import org.gridsuite.modification.dto.*; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import java.util.ArrayList; +import java.util.List; + +import static org.gridsuite.modification.NetworkModificationException.Type.*; +import static org.gridsuite.modification.utils.ModificationUtils.insertReportNode; + +/** + * @author Florent MILLOT + */ +public class TwoWindingsTransformerModification extends AbstractBranchModification { + + private static final String RATIO_TAP_CHANGER_SUBREPORTER_DEFAULT_MESSAGE = "Ratio tap changer"; + private static final String PHASE_TAP_CHANGER_SUBREPORTER_DEFAULT_MESSAGE = "Phase tap changer"; + public static final String MAGNETIZING_CONDUCTANCE_FIELD_NAME = "Magnetizing conductance"; + + public TwoWindingsTransformerModification(TwoWindingsTransformerModificationInfos modificationInfos) { + super(modificationInfos); + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (network.getTwoWindingsTransformer(modificationInfos.getEquipmentId()) == null) { + throw new NetworkModificationException(TWO_WINDINGS_TRANSFORMER_NOT_FOUND, + "Two windings transformer with ID '" + modificationInfos.getEquipmentId() + "' does not exist in the network"); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + TwoWindingsTransformer twoWindingsTransformer = network.getTwoWindingsTransformer(modificationInfos.getEquipmentId()); + // modify the 2wt in the network + modifyTwoWindingsTransformer(twoWindingsTransformer, modificationInfos, subReportNode, network); + } + + private void modifyTwoWindingsTransformer(TwoWindingsTransformer twoWindingsTransformer, BranchModificationInfos twoWindingsTransformerModificationInfos, ReportNode subReportNode, Network network) { + modifyBranch(twoWindingsTransformer, twoWindingsTransformerModificationInfos, subReportNode, "twoWindingsTransformerModification", "TwoWindingsTransformer with id=${id} modified :"); + addTapChangersToTwoWindingsTransformer(network, (TwoWindingsTransformerModificationInfos) modificationInfos, twoWindingsTransformer, subReportNode); + PropertiesUtils.applyProperties(twoWindingsTransformer, subReportNode, modificationInfos.getProperties(), "TwoWindingsTransformerProperties"); + } + + @Override + protected void modifyCharacteristics(Branch branch, BranchModificationInfos branchModificationInfos, ReportNode subReportNode) { + TwoWindingsTransformer twoWindingsTransformer = (TwoWindingsTransformer) branch; + ReportNode characteristicsReporter = subReportNode.newReportNode().withMessageTemplate("characteristics", "Characteristics").add(); + + // Branch specific fields + modifyR(twoWindingsTransformer, branchModificationInfos.getR(), characteristicsReporter); + modifyX(twoWindingsTransformer, branchModificationInfos.getX(), characteristicsReporter); + + // Transformer specific fields + TwoWindingsTransformerModificationInfos twoWindingsTransformerModificationInfos = (TwoWindingsTransformerModificationInfos) branchModificationInfos; + modifyTransformerFields(twoWindingsTransformer, + twoWindingsTransformerModificationInfos.getG(), + twoWindingsTransformerModificationInfos.getB(), + twoWindingsTransformerModificationInfos.getRatedS(), + twoWindingsTransformerModificationInfos.getRatedU1(), + twoWindingsTransformerModificationInfos.getRatedU2(), + characteristicsReporter); + } + + public static void modifyTransformerFields(TwoWindingsTransformer transformer, + AttributeModification modifG, + AttributeModification modifB, + AttributeModification modifRatedS, + AttributeModification modifRatedU1, + AttributeModification modifRatedU2, + ReportNode reportNode) { + modifyG(transformer, modifG, reportNode); + modifyB(transformer, modifB, reportNode); + modifyRatedS(transformer, modifRatedS, reportNode); + modifyRatedU1(transformer, modifRatedU1, reportNode); + modifyRatedU2(transformer, modifRatedU2, reportNode); + } + + public static void modifyRatedU2(TwoWindingsTransformer transformer, AttributeModification modifRatedU2, ReportNode reportNode) { + if (modifRatedU2 != null && modifRatedU2.getValue() != null) { + if (reportNode != null) { + insertReportNode(reportNode, ModificationUtils.getInstance().buildModificationReport(transformer.getRatedU2(), + modifRatedU2.getValue(), "Rated Voltage (Side 2)", 1)); + } + transformer.setRatedU2(modifRatedU2.getValue()); + } + } + + public static void modifyRatedU1(TwoWindingsTransformer transformer, AttributeModification modifRatedU1, ReportNode reportNode) { + if (modifRatedU1 != null && modifRatedU1.getValue() != null) { + if (reportNode != null) { + insertReportNode(reportNode, ModificationUtils.getInstance().buildModificationReport(transformer.getRatedU1(), + modifRatedU1.getValue(), "Rated Voltage (Side 1)", 1)); + } + transformer.setRatedU1(modifRatedU1.getValue()); + } + } + + public static void modifyRatedS(TwoWindingsTransformer transformer, AttributeModification modifRatedS, ReportNode reportNode) { + if (modifRatedS != null && modifRatedS.getValue() != null) { + if (reportNode != null) { + insertReportNode(reportNode, ModificationUtils.getInstance().buildModificationReport(transformer.getRatedS(), + modifRatedS.getValue(), "Rated nominal power", 1)); + } + transformer.setRatedS(modifRatedS.getValue()); + } + } + + public static void modifyB(TwoWindingsTransformer transformer, AttributeModification modifB, ReportNode reportNode) { + if (modifB != null && modifB.getValue() != null) { + // convert reported value from siemens to microsiemens + if (reportNode != null) { + double oldMagnetizingSusceptanceToReport = transformer.getB() * Math.pow(10, 6); + double newMagnetizingSusceptanceToReport = modifB.getValue() * Math.pow(10, 6); + insertReportNode(reportNode, ModificationUtils.getInstance().buildModificationReport(oldMagnetizingSusceptanceToReport, + newMagnetizingSusceptanceToReport, "Magnetizing susceptance", 1)); + } + transformer.setB(modifB.getValue()); + } + } + + public static void modifyG(TwoWindingsTransformer transformer, AttributeModification modifG, ReportNode reportNode) { + if (modifG != null && modifG.getValue() != null) { + // convert reported value from siemens to microsiemens + if (reportNode != null) { + double oldMagnetizingConductanceToReport = transformer.getG() * Math.pow(10, 6); + double newMagnetizingConductanceToReport = modifG.getValue() * Math.pow(10, 6); + ReportNode gReportNode = ModificationUtils.getInstance().buildModificationReport( + oldMagnetizingConductanceToReport, + newMagnetizingConductanceToReport, + MAGNETIZING_CONDUCTANCE_FIELD_NAME, + 1); + insertReportNode(reportNode, gReportNode); + } + transformer.setG(modifG.getValue()); + } + } + + public static void modifyX(TwoWindingsTransformer twt, AttributeModification modifX, ReportNode reportNode) { + if (modifX != null && modifX.getValue() != null) { + if (reportNode != null) { + insertReportNode(reportNode, ModificationUtils.getInstance().buildModificationReport(twt.getX(), + modifX.getValue(), "Series reactance", 1)); + } + twt.setX(modifX.getValue()); + } + } + + public static void modifyR(TwoWindingsTransformer twt, AttributeModification modifR, ReportNode reportNode) { + if (modifR != null && modifR.getValue() != null) { + if (reportNode != null) { + insertReportNode(reportNode, ModificationUtils.getInstance().buildModificationReport(twt.getR(), + modifR.getValue(), "Series resistance", 1)); + } + twt.setR(modifR.getValue()); + } + } + + private void addTapChangersToTwoWindingsTransformer(Network network, TwoWindingsTransformerModificationInfos twoWindingsTransformerModificationInfos, TwoWindingsTransformer twt, ReportNode subReportNode) { + if (twt.hasRatioTapChanger() && twoWindingsTransformerModificationInfos.getRatioTapChanger().getEnabled() != null && Boolean.FALSE.equals(twoWindingsTransformerModificationInfos.getRatioTapChanger().getEnabled().getValue())) { + twt.getRatioTapChanger().remove(); + subReportNode.newReportNode() + .withMessageTemplate("RatioTapChangerRemoved", "The ratio tap changer has been removed") + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } else if (ratioTapChangerModified(twoWindingsTransformerModificationInfos.getRatioTapChanger())) { + processRatioTapChanger(network, twoWindingsTransformerModificationInfos, twt, subReportNode, twt.hasRatioTapChanger()); + } + + if (twt.hasPhaseTapChanger() && twoWindingsTransformerModificationInfos.getPhaseTapChanger().getEnabled() != null && Boolean.FALSE.equals(twoWindingsTransformerModificationInfos.getPhaseTapChanger().getEnabled().getValue())) { + twt.getPhaseTapChanger().remove(); + subReportNode.newReportNode() + .withMessageTemplate("PhaseTapChangerRemoved", "The phase tap changer has been removed") + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } else if (phaseTapChangerModified(twoWindingsTransformerModificationInfos.getPhaseTapChanger())) { + processPhaseTapChanger(network, twoWindingsTransformerModificationInfos, twt, subReportNode, twt.hasPhaseTapChanger()); + } + } + + private void processPhaseTapChanger(Network network, + TwoWindingsTransformerModificationInfos twoWindingsTransformerModificationInfos, + TwoWindingsTransformer twt, + ReportNode subReportNode, + boolean isModification) { + PhaseTapChanger phaseTapChanger = isModification ? twt.getPhaseTapChanger() : null; + PhaseTapChangerAdder phaseTapChangerAdder = isModification ? null : twt.newPhaseTapChanger(); + PhaseTapChangerModificationInfos phaseTapChangerInfos = twoWindingsTransformerModificationInfos + .getPhaseTapChanger(); + List regulationReports = new ArrayList<>(); + + processPhaseTapRegulation(phaseTapChanger, phaseTapChangerAdder, isModification, phaseTapChangerInfos.getRegulationMode(), + phaseTapChangerInfos.getRegulationValue(), phaseTapChangerInfos.getTargetDeadband(), regulationReports); + + processRegulatingTerminal(phaseTapChangerInfos, phaseTapChanger, phaseTapChangerAdder, regulationReports, + network, + twt, isModification); + + List positionsAndStepsReports = new ArrayList<>(); + processTapChangerPositionsAndSteps(phaseTapChanger, phaseTapChangerAdder, isModification, phaseTapChangerInfos.getLowTapPosition(), phaseTapChangerInfos.getTapPosition(), phaseTapChangerInfos.getSteps(), positionsAndStepsReports); + + if (!isModification) { + phaseTapChangerAdder.add(); + } + + if (!regulationReports.isEmpty() || !positionsAndStepsReports.isEmpty()) { + ReportNode phaseTapChangerSubreporter = ModificationUtils.getInstance().reportModifications(subReportNode, + regulationReports, TapChangerType.PHASE.name(), PHASE_TAP_CHANGER_SUBREPORTER_DEFAULT_MESSAGE + ); + if (phaseTapChangerSubreporter == null) { + phaseTapChangerSubreporter = subReportNode.newReportNode() + .withMessageTemplate(TapChangerType.PHASE.name(), PHASE_TAP_CHANGER_SUBREPORTER_DEFAULT_MESSAGE) + .add(); + } + ModificationUtils.getInstance().reportModifications(phaseTapChangerSubreporter, positionsAndStepsReports, + "phaseTapChangerPositionsAndStepsModification", " Tap Changer"); + } + } + + public static void processPhaseTapRegulation(PhaseTapChanger phaseTapChanger, + PhaseTapChangerAdder phaseTapChangerAdder, + boolean isModification, + AttributeModification regulationModeModification, + AttributeModification regulationValueModification, + AttributeModification targetDeadbandModification, + List regulationReports) throws NetworkModificationException { + AttributeModification finalRegulationModeModification = regulationModeModification; + AttributeModification finalTargetDeadbandModification = targetDeadbandModification; + AttributeModification finalRegulatingModification = null; + + // --- Regulation Mode impacts on regulating => pre-processing regulating --- // + if (isModification) { // modifying an existing extension + // modification check + if (regulationModeModification != null) { + PhaseTapChanger.RegulationMode oldRegulationMode = phaseTapChanger.getRegulationMode(); + PhaseTapChanger.RegulationMode newRegulationMode = regulationModeModification.getValue(); + double oldTargetDeadBand = phaseTapChanger.getTargetDeadband(); + + if (oldRegulationMode == PhaseTapChanger.RegulationMode.FIXED_TAP) { + // if new regulation mode is FIXED_TAP set regulating to false + if (newRegulationMode == PhaseTapChanger.RegulationMode.FIXED_TAP) { + finalRegulatingModification = AttributeModification.toAttributeModification(false, OperationType.SET); + } else { // new regulation mode is CURRENT_LIMITER or ACTIVE_POWER_CONTROL + // check required field + if (regulationValueModification == null) { + throw new NetworkModificationException(MODIFY_TWO_WINDINGS_TRANSFORMER_ERROR, "Regulation value is missing, phase tap changer can not regulate"); + } + + // set regulating to true + finalRegulatingModification = AttributeModification.toAttributeModification(true, OperationType.SET); + + // set default value for deadband + if (Double.isNaN(oldTargetDeadBand) && targetDeadbandModification == null) { + finalTargetDeadbandModification = AttributeModification.toAttributeModification(0.0, OperationType.SET); + } + } + } else { // previous regulation mode is CURRENT_LIMITER or ACTIVE_POWER_CONTROL + // if new regulation mode is FIXED_TAP we keep the old regulation mode and set regulating to false + if (newRegulationMode == PhaseTapChanger.RegulationMode.FIXED_TAP) { + finalRegulatingModification = AttributeModification.toAttributeModification(false, OperationType.SET); + finalRegulationModeModification = AttributeModification.toAttributeModification(oldRegulationMode, OperationType.SET); + } else { // new regulation mode is CURRENT_LIMITER or ACTIVE_POWER_CONTROL + // set regulating to true + finalRegulatingModification = AttributeModification.toAttributeModification(true, OperationType.SET); + // set default value for deadband + if (Double.isNaN(oldTargetDeadBand) && targetDeadbandModification == null) { + finalTargetDeadbandModification = AttributeModification.toAttributeModification(0.0, OperationType.SET); + } + } + } + } + } else { + // creation check + if (regulationModeModification == null) { + throw new NetworkModificationException(CREATE_TWO_WINDINGS_TRANSFORMER_ERROR, "Regulation mode is missing when creating tap phase changer"); + } + + PhaseTapChanger.RegulationMode regulationMode = regulationModeModification.getValue(); + if (regulationMode != PhaseTapChanger.RegulationMode.FIXED_TAP) { + finalRegulatingModification = AttributeModification.toAttributeModification(true, OperationType.SET); + if (regulationValueModification == null) { + throw new NetworkModificationException(CREATE_TWO_WINDINGS_TRANSFORMER_ERROR, + "Regulation value is missing when creating tap phase changer with regulation enabled (different from FIXED_TAP)"); + } + if (finalTargetDeadbandModification == null) { + finalTargetDeadbandModification = AttributeModification.toAttributeModification(0.0, OperationType.SET); + } + } else { + finalRegulatingModification = AttributeModification.toAttributeModification(false, OperationType.SET); + } + } + + //c--- apply changes after pre-processing of regulating --- // + setPhaseTapChangerRegulationAttributes(phaseTapChanger, phaseTapChangerAdder, isModification, + finalRegulationModeModification, regulationValueModification, finalTargetDeadbandModification, finalRegulatingModification, regulationReports); + + } + + private static void setPhaseTapChangerRegulationAttributes(PhaseTapChanger phaseTapChanger, + PhaseTapChangerAdder phaseTapChangerAdder, + boolean isModification, + AttributeModification regulationModeModification, + AttributeModification regulationValueModification, + AttributeModification targetDeadbandModification, + AttributeModification regulatingModification, + List regulationReports) { + // the order is important if regulation mode is set and regulation value or target dead band is null it will crash + PhaseTapChanger.RegulationMode regulationMode = regulationModeModification == null ? null : regulationModeModification.getValue(); + String fieldName = (regulationMode == PhaseTapChanger.RegulationMode.CURRENT_LIMITER) ? "Value" : "Flow set point"; + // Regulation value + ReportNode regulationValueReportNode = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport( + isModification ? phaseTapChanger::setRegulationValue : phaseTapChangerAdder::setRegulationValue, + isModification ? phaseTapChanger::getRegulationValue : () -> null, + regulationValueModification, fieldName, 1); + if (regulationReports != null && regulationValueReportNode != null) { + regulationReports.add(regulationValueReportNode); + } + // targetDeadBand + ReportNode targetDeadbandReportNode = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport( + isModification ? phaseTapChanger::setTargetDeadband : phaseTapChangerAdder::setTargetDeadband, + isModification ? phaseTapChanger::getTargetDeadband : () -> null, + targetDeadbandModification, "Target deadband", 1); + if (regulationReports != null && targetDeadbandReportNode != null) { + regulationReports.add(targetDeadbandReportNode); + } + + // RegulationMode + ReportNode regulationReportNode = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport( + isModification ? phaseTapChanger::setRegulationMode : phaseTapChangerAdder::setRegulationMode, + isModification ? phaseTapChanger::getRegulationMode : () -> null, + regulationModeModification, "Regulation mode", 1); + if (regulationReports != null && regulationReportNode != null) { + regulationReports.add(regulationReportNode); + } + + // Regulating + ReportNode regulatingReportNode = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport( + isModification ? phaseTapChanger::setRegulating : phaseTapChangerAdder::setRegulating, + isModification ? phaseTapChanger::isRegulating : () -> null, + regulatingModification, "Phase tap regulating", 1); + if (regulationReports != null && regulatingReportNode != null) { + regulationReports.add(regulatingReportNode); + } + } + + private void processRatioTapChanger(Network network, + TwoWindingsTransformerModificationInfos twoWindingsTransformerModificationInfos, + TwoWindingsTransformer twt, + ReportNode subReporter, + boolean isModification) { + RatioTapChangerModificationInfos ratioTapChangerInfos = twoWindingsTransformerModificationInfos + .getRatioTapChanger(); + RatioTapChanger ratioTapChanger = isModification ? twt.getRatioTapChanger() : null; + RatioTapChangerAdder ratioTapChangerAdder = isModification ? null : twt.newRatioTapChanger(); + List ratioTapChangerReports = new ArrayList<>(); + ReportNode tapChangingReport = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport( + isModification ? ratioTapChanger::setLoadTapChangingCapabilities + : ratioTapChangerAdder::setLoadTapChangingCapabilities, + isModification ? ratioTapChanger::hasLoadTapChangingCapabilities : () -> null, + ratioTapChangerInfos.getLoadTapChangingCapabilities(), "Load tap changing capabilities", 1); + if (tapChangingReport != null) { + ratioTapChangerReports.add(tapChangingReport); + } + processRegulating(ratioTapChangerInfos, ratioTapChanger, ratioTapChangerAdder, ratioTapChangerReports, isModification); + + List voltageRegulationReports = new ArrayList<>(); + processRatioVoltageRegulation(ratioTapChangerInfos, twt, ratioTapChanger, ratioTapChangerAdder, voltageRegulationReports, network, + isModification); + List positionsAndStepsReports = new ArrayList<>(); + processTapChangerPositionsAndSteps(ratioTapChanger, ratioTapChangerAdder, isModification, ratioTapChangerInfos.getLowTapPosition(), ratioTapChangerInfos.getTapPosition(), ratioTapChangerInfos.getSteps(), positionsAndStepsReports + ); + + if (!isModification) { + ratioTapChangerAdder.add(); + } + + if (!ratioTapChangerReports.isEmpty() || !voltageRegulationReports.isEmpty() + || !positionsAndStepsReports.isEmpty()) { + ReportNode ratioTapChangerReporter = ModificationUtils.getInstance().reportModifications(subReporter, + ratioTapChangerReports, TapChangerType.RATIO.name(), RATIO_TAP_CHANGER_SUBREPORTER_DEFAULT_MESSAGE); + if (ratioTapChangerReporter == null) { + ratioTapChangerReporter = subReporter.newReportNode() + .withMessageTemplate(TapChangerType.RATIO.name(), RATIO_TAP_CHANGER_SUBREPORTER_DEFAULT_MESSAGE) + .add(); + } + ModificationUtils.getInstance().reportModifications(ratioTapChangerReporter, voltageRegulationReports, + "ratioTapChangerVoltageRegulationModification", " Voltage regulation"); + ModificationUtils.getInstance().reportModifications(ratioTapChangerReporter, positionsAndStepsReports, + "ratioTapChangerPositionsAndStepsModification", " Tap Changer"); + } + } + + private void processRegulating(RatioTapChangerModificationInfos ratioTapChangerInfos, + RatioTapChanger ratioTapChanger, RatioTapChangerAdder ratioTapChangerAdder, + List ratioTapChangerReports, boolean isModification) { + if (ratioTapChangerInfos.getRegulating() != null && ratioTapChangerInfos.getRegulating().getValue() != null) { + boolean regulating = ratioTapChangerInfos.getRegulating().getValue(); + ReportNode voltageRegulationReport = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport( + isModification ? ratioTapChanger::setRegulating + : ratioTapChangerAdder::setRegulating, + isModification ? ratioTapChanger::isRegulating : () -> null, + ratioTapChangerInfos.getRegulating(), regulating ? "Voltage regulation" : "Fixed ratio", 1); + if (voltageRegulationReport != null) { + ratioTapChangerReports.add(voltageRegulationReport); + } + } + } + + private void processRatioVoltageRegulation(RatioTapChangerModificationInfos ratioTapChangerInfos, + TwoWindingsTransformer twt, + RatioTapChanger ratioTapChanger, + RatioTapChangerAdder ratioTapChangerAdder, + List voltageRegulationReports, + Network network, + boolean isModification) { + modifyTargets(ratioTapChanger, ratioTapChangerAdder, isModification, ratioTapChangerInfos.getTargetV(), ratioTapChangerInfos.getTargetDeadband(), voltageRegulationReports); + + processRegulatingTerminal(ratioTapChangerInfos, ratioTapChanger, ratioTapChangerAdder, voltageRegulationReports, + network, twt, isModification); + } + + public static void modifyTargets(RatioTapChanger ratioTapChanger, RatioTapChangerAdder ratioTapChangerAdder, boolean isModification, AttributeModification targetV, AttributeModification targetDeadband, List voltageRegulationReports) { + ReportNode targetVoltageReportNode = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport( + isModification ? ratioTapChanger::setTargetV + : ratioTapChangerAdder::setTargetV, + isModification ? ratioTapChanger::getTargetV : () -> null, + targetV, "Target voltage", 2); + + ReportNode targetDeadbandReportNode = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport( + isModification ? ratioTapChanger::setTargetDeadband + : ratioTapChangerAdder::setTargetDeadband, + isModification ? ratioTapChanger::getTargetDeadband : () -> null, + targetDeadband, "Target deadband", 2); + + if (voltageRegulationReports != null) { + if (targetVoltageReportNode != null) { + voltageRegulationReports.add(targetVoltageReportNode); + } + if (targetDeadbandReportNode != null) { + voltageRegulationReports.add(targetDeadbandReportNode); + } + } + } + + private void processRegulatingTerminal(TapChangerModificationInfos tapChangerModificationInfos, + TapChanger tapChanger, + TapChangerAdder tapChangerAdder, + List regulationReports, + Network network, + TwoWindingsTransformer twt, + boolean isModification) { + String oldVoltageLevel = null; + String oldEquipment = null; + + if (isModification && tapChanger.getRegulationTerminal() != null) { + oldVoltageLevel = tapChanger.getRegulationTerminal().getVoltageLevel().getId(); + oldEquipment = tapChanger.getRegulationTerminal().getConnectable().getType() + .name() + ":" + + tapChanger.getRegulationTerminal().getConnectable().getId(); + } + + if (tapChangerModificationInfos.getRegulationSide() != null + && tapChangerModificationInfos.getRegulationSide().getValue() != null) { + Terminal terminal = tapChangerModificationInfos.getRegulationSide().getValue() == RegulationSide.SIDE1 + ? twt.getTerminal1() + : twt.getTerminal2(); + setRegulatingTerminalInfos(tapChangerModificationInfos, terminal); + } + + if (tapChangerModificationInfos.getRegulatingTerminalId() != null + && tapChangerModificationInfos.getRegulatingTerminalType() != null + && tapChangerModificationInfos.getRegulatingTerminalVlId() != null) { + Terminal terminal = ModificationUtils.getInstance().getTerminalFromIdentifiable(network, + tapChangerModificationInfos.getRegulatingTerminalId().getValue(), + tapChangerModificationInfos.getRegulatingTerminalType().getValue(), + tapChangerModificationInfos.getRegulatingTerminalVlId().getValue()); + if (isModification) { + tapChanger.setRegulationTerminal(terminal); + } else { + tapChangerAdder.setRegulationTerminal(terminal); + } + regulationReports + .add(ModificationUtils.getInstance().buildModificationReport(oldVoltageLevel, + tapChangerModificationInfos.getRegulatingTerminalVlId().getValue(), + "Voltage level", 2)); + regulationReports.add(ModificationUtils.getInstance().buildModificationReport(oldEquipment, + tapChangerModificationInfos.getRegulatingTerminalType().getValue() + ":" + + tapChangerModificationInfos.getRegulatingTerminalId().getValue(), + "Equipment", 2)); + } + } + + private void setRegulatingTerminalInfos(TapChangerModificationInfos tapChangerModificationInfos, Terminal terminal) { + tapChangerModificationInfos.setRegulatingTerminalVlId(new AttributeModification<>(terminal.getVoltageLevel().getId(), OperationType.SET)); + tapChangerModificationInfos.setRegulatingTerminalId(new AttributeModification<>(terminal.getConnectable().getId(), OperationType.SET)); + tapChangerModificationInfos.setRegulatingTerminalType(new AttributeModification<>(terminal.getConnectable().getType().name(), OperationType.SET)); + } + + private static void processTapchangerSteps(List tapChangerStepsReports, + TapChangerAdder tapChangerAdder, + TapChangerStepsReplacer tapChangerStepReplacer, + boolean isModification, + List modifSteps) { + if (tapChangerStepsReports != null) { + tapChangerStepsReports.add(ReportNode.newRootReportNode() + .withMessageTemplate("tapChangerStepsModification", " Taps were replaced by new ones below") + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + } + for (TapChangerStepCreationInfos step : modifSteps) { + if (tapChangerStepsReports != null) { + addStepAttributeReports(tapChangerStepsReports, step); + } + if (tapChangerStepReplacer instanceof RatioTapChangerStepsReplacer || tapChangerAdder instanceof RatioTapChangerAdder) { + processRatioTapChangerStep(tapChangerAdder, tapChangerStepReplacer, isModification, step); + } else { + processPhaseTapChangerStep(tapChangerStepsReports, (PhaseTapChangerAdder) tapChangerAdder, (PhaseTapChangerStepsReplacer) tapChangerStepReplacer, isModification, step); + } + } + if (isModification) { + tapChangerStepReplacer.replaceSteps(); + } + } + + private static void processPhaseTapChangerStep(List tapChangerStepsReports, PhaseTapChangerAdder tapChangerAdder, PhaseTapChangerStepsReplacer tapChangerStepReplacer, boolean isModification, TapChangerStepCreationInfos step) { + if (tapChangerStepsReports != null) { + addStepAttributeReport(tapChangerStepsReports, "newStepAlpha" + step.getAlpha(), + " Shift angle : ${alpha}", "alpha", String.valueOf(step.getAlpha())); + } + if (isModification) { + tapChangerStepReplacer.beginStep().setR(step.getR()).setX(step.getX()).setG(step.getG()) + .setB(step.getB()).setRho(step.getRho()).setAlpha(step.getAlpha()).endStep(); + } else { + tapChangerAdder.beginStep().setR(step.getR()).setX(step.getX()).setG(step.getG()) + .setB(step.getB()).setRho(step.getRho()).setAlpha(step.getAlpha()).endStep(); + } + } + + private static void processRatioTapChangerStep(TapChangerAdder tapChangerAdder, TapChangerStepsReplacer tapChangerStepReplacer, boolean isModification, TapChangerStepCreationInfos step) { + if (isModification) { + tapChangerStepReplacer.beginStep().setR(step.getR()).setX(step.getX()).setG(step.getG()) + .setB(step.getB()).setRho(step.getRho()).endStep(); + } else { + tapChangerAdder.beginStep().setR(step.getR()).setX(step.getX()).setG(step.getG()) + .setB(step.getB()).setRho(step.getRho()).endStep(); + } + } + + private static void addStepAttributeReports(List tapChangerStepsReports, TapChangerStepCreationInfos step) { + addStepAttributeReport(tapChangerStepsReports, "newStepIndex" + step.getIndex(), + " Tap (${index})", "index", String.valueOf(step.getIndex())); + addStepAttributeReport(tapChangerStepsReports, "newStepResistance" + step.getR(), + " Δ resistance : ${r}", "r", String.valueOf(step.getR())); + addStepAttributeReport(tapChangerStepsReports, "newStepReactance" + step.getX(), + " Δ reactance : ${x}", "x", String.valueOf(step.getX())); + addStepAttributeReport(tapChangerStepsReports, "newStepConductance" + step.getG(), + " Δ conductance : ${g}", "g", String.valueOf(step.getG())); + addStepAttributeReport(tapChangerStepsReports, "newStepSusceptance" + step.getB(), + " Δ susceptance : ${b}", "b", String.valueOf(step.getB())); + addStepAttributeReport(tapChangerStepsReports, "newStepRatio" + step.getRho(), + " Ratio : ${rho}", "rho", String.valueOf(step.getRho())); + } + + public static void processTapChangerPositionsAndSteps(TapChanger tapChanger, + TapChangerAdder tapChangerAdder, + boolean isModification, + AttributeModification modifyLowTapPosition, + AttributeModification modifyTapPosition, + List modifySteps, + List tapChangerReports) { + ReportNode lowTapPositionReportNode = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport( + isModification ? tapChanger::setLowTapPosition + : tapChangerAdder::setLowTapPosition, + isModification ? tapChanger::getLowTapPosition : () -> null, + modifyLowTapPosition, "Low tap position", 2); + + ReportNode tapPositionReportNode = ModificationUtils.getInstance().applyElementaryModificationsAndReturnReport( + isModification ? tapChanger::setTapPosition + : tapChangerAdder::setTapPosition, + isModification ? tapChanger::getTapPosition : () -> null, + modifyTapPosition, "Tap position", 2); + + if (tapChangerReports != null) { + if (lowTapPositionReportNode != null) { + tapChangerReports.add(lowTapPositionReportNode); + } + if (tapPositionReportNode != null) { + tapChangerReports.add(tapPositionReportNode); + } + } + + // Add steps + if (modifySteps != null) { + if (tapChangerReports != null) { + tapChangerReports.add(ReportNode.newRootReportNode() + .withMessageTemplate("tapsModification", " Taps") + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + } + processTapchangerSteps(tapChangerReports, + tapChangerAdder, isModification ? tapChanger.stepsReplacer() : null, isModification, modifySteps); + } + } + + private static void addStepAttributeReport(List tapChangerStepsReports, String key, String defaultMessage, + String valueKey, String value) { + tapChangerStepsReports.add(ReportNode.newRootReportNode() + .withMessageTemplate(key, defaultMessage) + .withUntypedValue(valueKey, value) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + } + + private boolean ratioTapChangerModified(RatioTapChangerModificationInfos ratioTapChangerModificationInfos) { + return ratioTapChangerModificationInfos != null && ( + ratioTapChangerModificationInfos.getLoadTapChangingCapabilities() != null + && ratioTapChangerModificationInfos.getLoadTapChangingCapabilities().getValue() != null + || ratioTapChangerModificationInfos.getTargetV() != null + && ratioTapChangerModificationInfos.getTargetV().getValue() != null + || commonTapChangerAttributesModified(ratioTapChangerModificationInfos)); + } + + private boolean phaseTapChangerModified(PhaseTapChangerModificationInfos phaseTapChangerModificationInfos) { + return phaseTapChangerModificationInfos != null && ( + phaseTapChangerModificationInfos.getRegulationMode() != null + && phaseTapChangerModificationInfos.getRegulationMode().getValue() != null + || phaseTapChangerModificationInfos.getRegulationValue() != null + && phaseTapChangerModificationInfos.getRegulationValue().getValue() != null + || commonTapChangerAttributesModified(phaseTapChangerModificationInfos)); + } + + private boolean commonTapChangerAttributesModified(TapChangerModificationInfos tapChangerModificationInfos) { + return tapChangerModificationInfos != null && ( + tapChangerModificationInfos.getRegulating() != null + && tapChangerModificationInfos.getRegulating().getValue() != null + || tapChangerModificationInfos.getRegulationType() != null + && tapChangerModificationInfos.getRegulationType().getValue() != null + || tapChangerModificationInfos.getRegulationSide() != null + && tapChangerModificationInfos.getRegulationSide().getValue() != null + || tapChangerModificationInfos.getRegulatingTerminalId() != null + && tapChangerModificationInfos.getRegulatingTerminalId().getValue() != null + || tapChangerModificationInfos.getRegulatingTerminalType() != null + && tapChangerModificationInfos.getRegulatingTerminalType().getValue() != null + || tapChangerModificationInfos.getRegulatingTerminalVlId() != null + && tapChangerModificationInfos.getRegulatingTerminalVlId().getValue() != null + || tapChangerModificationInfos.getTargetDeadband() != null + && tapChangerModificationInfos.getTargetDeadband().getValue() != null + || positionsAndStepsModified(tapChangerModificationInfos)); + } + + private boolean positionsAndStepsModified(TapChangerModificationInfos tapChangerModificationInfos) { + return tapChangerModificationInfos.getTapPosition() != null + && tapChangerModificationInfos.getTapPosition().getValue() != null + || tapChangerModificationInfos.getLowTapPosition() != null + && tapChangerModificationInfos.getLowTapPosition().getValue() != null + || tapChangerModificationInfos.getSteps() != null; + } + + @Override + protected boolean characteristicsModified(BranchModificationInfos branchModificationInfos) { + TwoWindingsTransformerModificationInfos twoWindingsTransformerModificationInfos = (TwoWindingsTransformerModificationInfos) branchModificationInfos; + return super.characteristicsModified(branchModificationInfos) + || twoWindingsTransformerModificationInfos.getG() != null + && twoWindingsTransformerModificationInfos.getG().getValue() != null + || twoWindingsTransformerModificationInfos.getB() != null + && twoWindingsTransformerModificationInfos.getB().getValue() != null + || twoWindingsTransformerModificationInfos.getRatedU1() != null + && twoWindingsTransformerModificationInfos.getRatedU1().getValue() != null + || twoWindingsTransformerModificationInfos.getRatedU2() != null + && twoWindingsTransformerModificationInfos.getRatedU2().getValue() != null + || twoWindingsTransformerModificationInfos.getRatedS() != null + && twoWindingsTransformerModificationInfos.getRatedS().getValue() != null; + } + +} diff --git a/src/main/java/org/gridsuite/modification/modifications/VoltageInitModification.java b/src/main/java/org/gridsuite/modification/modifications/VoltageInitModification.java new file mode 100644 index 0000000..1913293 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/VoltageInitModification.java @@ -0,0 +1,431 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportConstants; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import lombok.AllArgsConstructor; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.NetworkModificationException.Type; +import org.gridsuite.modification.dto.*; +import org.gridsuite.modification.utils.ModificationUtils; + +import static org.gridsuite.modification.utils.ModificationUtils.insertReportNode; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Franck Lecuyer + */ +@AllArgsConstructor +public class VoltageInitModification extends AbstractModification { + private final VoltageInitModificationInfos voltageInitModificationInfos; + + private static final String GENERATORS_KEY = "GeneratorsModifications"; + private static final String GENERATORS_NAME = "Generators"; + private static final String TWO_WINDINGS_TRANSFORMERS_KEY = "2WindingsTransformersModifications"; + private static final String TWO_WINDINGS_TRANSFORMERS_NAME = "2 windings transformers"; + private static final String THREE_WINDINGS_TRANSFORMERS_KEY = "3WindingsTransformersModifications"; + private static final String THREE_WINDINGS_TRANSFORMERS_NAME = "3 windings transformers"; + private static final String STATIC_VAR_COMPENSATORS_KEY = "StaticVarCompensatorsModifications"; + private static final String STATIC_VAR_COMPENSATORS_NAME = "Static var compensators"; + private static final String VSC_CONVERTER_STATIONS_KEY = "VscConverterStationsModifications"; + private static final String VSC_CONVERTER_STATIONS_NAME = "Vsc converter stations"; + private static final String SHUNT_COMPENSATORS_KEY = "ShuntCompensatorsModifications"; + private static final String SHUNT_COMPENSATORS_NAME = "Shunt compensators"; + + private static final String VOLTAGE_SET_POINT = "Voltage set point"; + private static final String VOLTAGE_MAGNITUDE = "Voltage magnitude"; + private static final String VOLTAGE_ANGLE = "Voltage angle"; + private static final String REACTIVE_POWER_SET_POINT = "Reactive power set point"; + private static final String SECTION_COUNT = "Section count"; + private static final String COUNT = "count"; + + @Override + public void check(Network network) throws NetworkModificationException { + if (voltageInitModificationInfos == null) { + throw new NetworkModificationException(Type.VOLTAGE_INIT_MODIFICATION_ERROR, "No voltage init modification to apply !!"); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + // apply generators modifications + applyGeneratorModification(network, subReportNode); + + // apply transformers modifications + applyTransformerModification(network, subReportNode); + + // apply static var compensators modifications + applyStaticVarCompensatorModification(network, subReportNode); + + // apply shunt compensators modifications + applyShuntCompensatorModification(network, subReportNode); + + // apply vsc converter stations modifications + applyVscConverterStationModification(network, subReportNode); + + // apply buses modifications + applyBusModification(network, subReportNode); + } + + private void applyBusModification(Network network, ReportNode subReportNode) { + int modificationsCount = 0; + List reports = new ArrayList<>(); + for (VoltageInitBusModificationInfos m : voltageInitModificationInfos.getBuses()) { + String voltageLevelId = m.getVoltageLevelId(); + Bus bus; + if (voltageLevelId != null) { + final VoltageLevel voltageLevel = network.getVoltageLevel(voltageLevelId); + bus = voltageLevel.getBusView().getBus(m.getBusId()); + } else { + bus = network.getBusView().getBus(m.getBusId()); + } + if (bus == null) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("busNotFound", "Bus with id=${id} not found") + .withUntypedValue("id", m.getBusId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else if (m.getV() != null || m.getAngle() != null) { + modificationsCount++; + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("busModification", "Bus with id=${id} modified :") + .withUntypedValue("id", m.getBusId()) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + if (m.getV() != null) { + final double oldV = bus.getV(); + bus.setV(m.getV()); + reports.add(ModificationUtils.buildModificationReport(oldV, m.getV(), VOLTAGE_MAGNITUDE, 1, + TypedValue.TRACE_SEVERITY)); + } + if (m.getAngle() != null) { + final double oldAngle = bus.getAngle(); + bus.setAngle(m.getAngle()); + reports.add(ModificationUtils.buildModificationReport(oldAngle, m.getAngle(), VOLTAGE_ANGLE, 1, + TypedValue.TRACE_SEVERITY)); + } + } + } + if (!reports.isEmpty()) { + ReportNode busesReporter = subReportNode.newReportNode().withMessageTemplate("BusesModifications", "Buses").add(); + reports.forEach(report -> insertReportNode(busesReporter, report)); + } + if (modificationsCount > 0) { + subReportNode.newReportNode() + .withMessageTemplate("busModificationsResume", "${count} bus(es) have been modified.") + .withUntypedValue(COUNT, modificationsCount) + .withTypedValue(ReportConstants.SEVERITY_KEY, TypedValue.INFO_SEVERITY.toString(), TypedValue.SEVERITY) + .add(); + } + } + + private void applyGeneratorModification(Network network, ReportNode subReportNode) { + int modificationsCount = 0; + List reports = new ArrayList<>(); + for (final VoltageInitGeneratorModificationInfos m : voltageInitModificationInfos.getGenerators()) { + final Generator generator = network.getGenerator(m.getGeneratorId()); + if (generator == null) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("generatorNotFound", "Generator with id=${id} not found") + .withUntypedValue("id", m.getGeneratorId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else if (m.getTargetV() != null || m.getTargetQ() != null) { + modificationsCount++; + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("generatorModification", "Generator with id=${id} modified :") + .withUntypedValue("id", m.getGeneratorId()) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + if (m.getTargetV() != null) { + final double oldTargetV = generator.getTargetV(); + generator.setTargetV(m.getTargetV()); + reports.add(ModificationUtils.buildModificationReport(oldTargetV, m.getTargetV(), VOLTAGE_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + } + if (m.getTargetQ() != null) { + final double oldTargetQ = generator.getTargetQ(); + generator.setTargetQ(m.getTargetQ()); + reports.add(ModificationUtils.buildModificationReport(oldTargetQ, m.getTargetQ(), REACTIVE_POWER_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + } + } + } + if (!reports.isEmpty()) { + ReportNode generatorsReportNode = subReportNode.newReportNode().withMessageTemplate(GENERATORS_KEY, GENERATORS_NAME).add(); + reports.forEach(report -> insertReportNode(generatorsReportNode, report)); + } + if (modificationsCount > 0) { + subReportNode.newReportNode() + .withMessageTemplate("generatorModificationsResume", "${count} generator(s) have been modified.") + .withUntypedValue(COUNT, modificationsCount) + .withTypedValue(ReportConstants.SEVERITY_KEY, TypedValue.INFO_SEVERITY.toString(), TypedValue.SEVERITY) + .add(); + } + } + + private void applyTransformerModification(Network network, ReportNode subReportNode) { + int modificationsCount = 0; + List reports2WT = new ArrayList<>(); + List reports3WT = new ArrayList<>(); + for (final VoltageInitTransformerModificationInfos t : voltageInitModificationInfos.getTransformers()) { + if (t.getLegSide() != null) { + final ThreeWindingsTransformer threeWindingsTransformer = network.getThreeWindingsTransformer(t.getTransformerId()); + if (threeWindingsTransformer == null) { + reports3WT.add(ReportNode.newRootReportNode() + .withMessageTemplate("3WindingsTransformerNotFound", "3 windings transformer with id=${id} not found") + .withUntypedValue("id", t.getTransformerId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else if (threeWindingsTransformer.getLeg(t.getLegSide()).getRatioTapChanger() == null) { + reports3WT.add(ReportNode.newRootReportNode() + .withMessageTemplate("3WindingsTransformerRatioTapChangerNotFound", "3 windings transformer with id=${id} : Ratio tap changer for leg ${leg} not found") + .withUntypedValue("id", t.getTransformerId()) + .withUntypedValue("leg", t.getLegSide().name()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else if (t.getRatioTapChangerPosition() != null || t.getRatioTapChangerTargetV() != null) { + modificationsCount++; + reports3WT.add(ReportNode.newRootReportNode() + .withMessageTemplate("3WindingsTransformerModification", "3 windings transformer with id=${id} modified :") + .withUntypedValue("id", t.getTransformerId()) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + if (t.getRatioTapChangerPosition() != null) { + final int oldTapPosition = threeWindingsTransformer.getLeg(t.getLegSide()).getRatioTapChanger().getTapPosition(); + threeWindingsTransformer.getLeg(t.getLegSide()).getRatioTapChanger().setTapPosition(t.getRatioTapChangerPosition()); + reports3WT.add(ModificationUtils.buildModificationReport(oldTapPosition, t.getRatioTapChangerPosition(), "Leg " + t.getLegSide().name() + " ratio tap changer position", 1, TypedValue.TRACE_SEVERITY)); + } + if (t.getRatioTapChangerTargetV() != null) { + final double oldTapTargetV = threeWindingsTransformer.getLeg(t.getLegSide()).getRatioTapChanger().getTargetV(); + threeWindingsTransformer.getLeg(t.getLegSide()).getRatioTapChanger().setTargetV(t.getRatioTapChangerTargetV()); + reports3WT.add(ModificationUtils.buildModificationReport(oldTapTargetV, t.getRatioTapChangerTargetV(), "Leg " + t.getLegSide().name() + " ratio tap changer target voltage", 1, TypedValue.TRACE_SEVERITY)); + } + } + } else { + final TwoWindingsTransformer twoWindingsTransformer = network.getTwoWindingsTransformer(t.getTransformerId()); + if (twoWindingsTransformer == null) { + reports2WT.add(ReportNode.newRootReportNode() + .withMessageTemplate("2WindingsTransformerNotFound", "2 windings transformer with id=${id} not found") + .withUntypedValue("id", t.getTransformerId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else if (twoWindingsTransformer.getRatioTapChanger() == null) { + reports2WT.add(ReportNode.newRootReportNode() + .withMessageTemplate("2WindingsTransformerRatioTapChangerNotFound", "2 windings transformer with id=${id} : Ratio tap changer not found") + .withUntypedValue("id", t.getTransformerId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else if (t.getRatioTapChangerPosition() != null || t.getRatioTapChangerTargetV() != null) { + modificationsCount++; + reports2WT.add(ReportNode.newRootReportNode() + .withMessageTemplate("2WindingsTransformerModification", "2 windings transformer with id=${id} modified :") + .withUntypedValue("id", t.getTransformerId()) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + if (t.getRatioTapChangerPosition() != null) { + final int oldTapPosition = twoWindingsTransformer.getRatioTapChanger().getTapPosition(); + twoWindingsTransformer.getRatioTapChanger().setTapPosition(t.getRatioTapChangerPosition()); + reports2WT.add(ModificationUtils.buildModificationReport(oldTapPosition, t.getRatioTapChangerPosition(), "Ratio tap changer position", 1, TypedValue.TRACE_SEVERITY)); + } + if (t.getRatioTapChangerTargetV() != null) { + final double oldTapTargetV = twoWindingsTransformer.getRatioTapChanger().getTargetV(); + twoWindingsTransformer.getRatioTapChanger().setTargetV(t.getRatioTapChangerTargetV()); + reports2WT.add(ModificationUtils.buildModificationReport(oldTapTargetV, t.getRatioTapChangerTargetV(), "Ratio tap changer target voltage", 1, TypedValue.TRACE_SEVERITY)); + } + } + } + } + if (!reports2WT.isEmpty()) { + ReportNode twoWindingsTransformerReportNode = subReportNode.newReportNode().withMessageTemplate(TWO_WINDINGS_TRANSFORMERS_KEY, TWO_WINDINGS_TRANSFORMERS_NAME).add(); + reports2WT.forEach(report -> insertReportNode(twoWindingsTransformerReportNode, report)); + } + if (!reports3WT.isEmpty()) { + ReportNode threeWindingsTransformerReporter = subReportNode.newReportNode().withMessageTemplate(THREE_WINDINGS_TRANSFORMERS_KEY, THREE_WINDINGS_TRANSFORMERS_NAME).add(); + reports3WT.forEach(report -> insertReportNode(threeWindingsTransformerReporter, report)); + } + if (modificationsCount > 0) { + subReportNode.newReportNode() + .withMessageTemplate("windingsTransformerModificationsResume", "${count} transformer(s) have been modified.") + .withUntypedValue(COUNT, modificationsCount) + .withTypedValue(ReportConstants.SEVERITY_KEY, TypedValue.INFO_SEVERITY.toString(), TypedValue.SEVERITY) + .add(); + } + } + + private void applyStaticVarCompensatorModification(Network network, ReportNode subReportNode) { + int modificationsCount = 0; + List reports = new ArrayList<>(); + for (VoltageInitStaticVarCompensatorModificationInfos s : voltageInitModificationInfos.getStaticVarCompensators()) { + final StaticVarCompensator staticVarCompensator = network.getStaticVarCompensator(s.getStaticVarCompensatorId()); + if (staticVarCompensator == null) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("staticVarCompensatorNotFound", "Static var compensator with id=${id} not found") + .withUntypedValue("id", s.getStaticVarCompensatorId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else if (s.getVoltageSetpoint() != null || s.getReactivePowerSetpoint() != null) { + modificationsCount++; + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("staticVarCompensatorModification", "Static var compensator with id=${id} modified :") + .withUntypedValue("id", s.getStaticVarCompensatorId()) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + if (s.getVoltageSetpoint() != null) { + final double oldTargetV = staticVarCompensator.getVoltageSetpoint(); + staticVarCompensator.setVoltageSetpoint(s.getVoltageSetpoint()); + reports.add(ModificationUtils.buildModificationReport(oldTargetV, s.getVoltageSetpoint(), VOLTAGE_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + } + if (s.getReactivePowerSetpoint() != null) { + final double oldTargetQ = staticVarCompensator.getReactivePowerSetpoint(); + staticVarCompensator.setReactivePowerSetpoint(s.getReactivePowerSetpoint()); + reports.add(ModificationUtils.buildModificationReport(oldTargetQ, s.getReactivePowerSetpoint(), REACTIVE_POWER_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + } + } + } + if (!reports.isEmpty()) { + ReportNode staticVarsReportNode = subReportNode.newReportNode().withMessageTemplate(STATIC_VAR_COMPENSATORS_KEY, STATIC_VAR_COMPENSATORS_NAME).add(); + reports.forEach(report -> insertReportNode(staticVarsReportNode, report)); + } + if (modificationsCount > 0) { + subReportNode.newReportNode() + .withMessageTemplate("svcModificationsResume", "${count} static var compensator(s) have been modified.") + .withUntypedValue(COUNT, modificationsCount) + .withTypedValue(ReportConstants.SEVERITY_KEY, TypedValue.INFO_SEVERITY.toString(), TypedValue.SEVERITY) + .add(); + } + } + + private void applyVscConverterStationModification(Network network, ReportNode subReportNode) { + int modificationsCount = 0; + List reports = new ArrayList<>(); + for (VoltageInitVscConverterStationModificationInfos v : voltageInitModificationInfos.getVscConverterStations()) { + final VscConverterStation vscConverterStation = network.getVscConverterStation(v.getVscConverterStationId()); + if (vscConverterStation == null) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("vscConverterStationNotFound", "Vsc converter station with id=${id} not found") + .withUntypedValue("id", v.getVscConverterStationId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else if (v.getVoltageSetpoint() != null || v.getReactivePowerSetpoint() != null) { + modificationsCount++; + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("vscConverterStationModification", "Vsc converter station with id=${id} modified :") + .withUntypedValue("id", v.getVscConverterStationId()) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + if (v.getVoltageSetpoint() != null) { + final double oldTargetV = vscConverterStation.getVoltageSetpoint(); + vscConverterStation.setVoltageSetpoint(v.getVoltageSetpoint()); + reports.add(ModificationUtils.buildModificationReport(oldTargetV, v.getVoltageSetpoint(), VOLTAGE_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + } + if (v.getReactivePowerSetpoint() != null) { + final double oldTargetQ = vscConverterStation.getReactivePowerSetpoint(); + vscConverterStation.setReactivePowerSetpoint(v.getReactivePowerSetpoint()); + reports.add(ModificationUtils.buildModificationReport(oldTargetQ, v.getReactivePowerSetpoint(), REACTIVE_POWER_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + } + } + } + if (!reports.isEmpty()) { + ReportNode vscConverterStationsReporter = subReportNode.newReportNode().withMessageTemplate(VSC_CONVERTER_STATIONS_KEY, VSC_CONVERTER_STATIONS_NAME).add(); + reports.forEach(report -> insertReportNode(vscConverterStationsReporter, report)); + } + if (modificationsCount > 0) { + subReportNode.newReportNode() + .withMessageTemplate("vscModificationsResume", "${count} vsc converter station(s) have been modified.") + .withUntypedValue(COUNT, modificationsCount) + .withTypedValue(ReportConstants.SEVERITY_KEY, TypedValue.INFO_SEVERITY.toString(), TypedValue.SEVERITY) + .add(); + } + } + + private void applyShuntCompensatorModification(Network network, ReportNode subReportNode) { + int modificationsCount = 0; + List reports = new ArrayList<>(); + for (VoltageInitShuntCompensatorModificationInfos m : voltageInitModificationInfos.getShuntCompensators()) { + final ShuntCompensator shuntCompensator = network.getShuntCompensator(m.getShuntCompensatorId()); + if (shuntCompensator == null) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("shuntCompensatorNotFound", "Shunt compensator with id=${id} not found") + .withUntypedValue("id", m.getShuntCompensatorId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else if (m.getSectionCount() != null || m.getConnect() != null || m.getTargetV() != null) { + List reportsShunt = new ArrayList<>(); + final int currentSectionCount = shuntCompensator.getSectionCount(); + final Terminal shuntCompensatorTerminal = shuntCompensator.getTerminal(); + if (shuntCompensatorTerminal.isConnected()) { // shunt compensator is connected + if (m.getSectionCount() == null) { + reportsShunt.add(ReportNode.newRootReportNode() + .withMessageTemplate("shuntCompensatorSectionCountUndefined", "\tSection count value is undefined") + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else { + if (m.getSectionCount() == 0) { + shuntCompensatorTerminal.disconnect(); + reportsShunt.add(ReportNode.newRootReportNode() + .withMessageTemplate("shuntCompensatorDisconnected", "\tShunt compensator disconnected") + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + } + if (m.getSectionCount() != currentSectionCount) { + shuntCompensator.setSectionCount(m.getSectionCount()); + reportsShunt.add(ModificationUtils.buildModificationReport(currentSectionCount, m.getSectionCount(), SECTION_COUNT, 1, TypedValue.TRACE_SEVERITY)); + } + } + } else { // shunt compensator is disconnected + if (m.getConnect() == null) { + reportsShunt.add(ReportNode.newRootReportNode() + .withMessageTemplate("shuntCompensatorConnectUndefined", "\tConnect value is undefined") + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else { + if (Boolean.TRUE.equals(m.getConnect())) { + shuntCompensatorTerminal.connect(); + reportsShunt.add(ReportNode.newRootReportNode() + .withMessageTemplate("shuntCompensatorReconnected", "\tShunt compensator reconnected") + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + } + if (m.getSectionCount() != currentSectionCount) { + shuntCompensator.setSectionCount(m.getSectionCount()); + reportsShunt.add(ModificationUtils.buildModificationReport(currentSectionCount, m.getSectionCount(), SECTION_COUNT, 1, TypedValue.TRACE_SEVERITY)); + } + } + } + if (m.getTargetV() != null) { + final double oldTargetV = shuntCompensator.getTargetV(); + shuntCompensator.setTargetV(m.getTargetV()); + reportsShunt.add(ModificationUtils.buildModificationReport(oldTargetV, m.getTargetV(), VOLTAGE_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + } + if (!reportsShunt.isEmpty()) { + modificationsCount++; + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("shuntCompensatorModification", "Shunt compensator with id=${id} modified :") + .withUntypedValue("id", m.getShuntCompensatorId()) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + reports.addAll(reportsShunt); + } + } + } + if (!reports.isEmpty()) { + ReportNode shuntCompensatorsReporter = subReportNode.newReportNode().withMessageTemplate(SHUNT_COMPENSATORS_KEY, SHUNT_COMPENSATORS_NAME).add(); + reports.forEach(report -> insertReportNode(shuntCompensatorsReporter, report)); + } + if (modificationsCount > 0) { + subReportNode.newReportNode() + .withMessageTemplate("shuntCompensatorModificationsResume", "${count} shunt compensator(s) have been modified.") + .withUntypedValue(COUNT, modificationsCount) + .withTypedValue(ReportConstants.SEVERITY_KEY, TypedValue.INFO_SEVERITY.toString(), TypedValue.SEVERITY) + .add(); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/VoltageLevelCreation.java b/src/main/java/org/gridsuite/modification/modifications/VoltageLevelCreation.java new file mode 100644 index 0000000..f4c46a4 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/VoltageLevelCreation.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.VoltageLevel; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.VoltageLevelCreationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +/** + * @author walid Sahnoun + */ +public class VoltageLevelCreation extends AbstractModification { + + private final VoltageLevelCreationInfos modificationInfos; + + public VoltageLevelCreation(VoltageLevelCreationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + ModificationUtils.getInstance().controlVoltageLevelCreation(modificationInfos, network); + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + ModificationUtils.getInstance().createVoltageLevel(modificationInfos, subReportNode, network); + // properties + VoltageLevel voltageLevel = network.getVoltageLevel(modificationInfos.getEquipmentId()); + PropertiesUtils.applyProperties(voltageLevel, subReportNode, modificationInfos.getProperties(), "VlProperties"); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/VoltageLevelModification.java b/src/main/java/org/gridsuite/modification/modifications/VoltageLevelModification.java new file mode 100644 index 0000000..e444df3 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/VoltageLevelModification.java @@ -0,0 +1,166 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.IdentifiableShortCircuit; +import com.powsybl.iidm.network.extensions.IdentifiableShortCircuitAdder; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.AttributeModification; +import org.gridsuite.modification.dto.VoltageLevelModificationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static org.gridsuite.modification.NetworkModificationException.Type.MODIFY_VOLTAGE_LEVEL_ERROR; +import static org.gridsuite.modification.utils.ModificationUtils.insertReportNode; + +/** + * @author Seddik Yengui + */ + +public class VoltageLevelModification extends AbstractModification { + private final VoltageLevelModificationInfos modificationInfos; + + public VoltageLevelModification(VoltageLevelModificationInfos voltageLevelModificationInfos) { + this.modificationInfos = voltageLevelModificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + boolean ipMinSet = false; + boolean ipMaxSet = false; + if (Objects.nonNull(modificationInfos.getIpMin())) { + ipMinSet = true; + if (modificationInfos.getIpMin().getValue() < 0) { + throw new NetworkModificationException(MODIFY_VOLTAGE_LEVEL_ERROR, "IpMin must be positive"); + } + } + if (Objects.nonNull(modificationInfos.getIpMax())) { + ipMaxSet = true; + if (modificationInfos.getIpMax().getValue() < 0) { + throw new NetworkModificationException(MODIFY_VOLTAGE_LEVEL_ERROR, "IpMax must be positive"); + } + } + if (ipMinSet && ipMaxSet) { + if (modificationInfos.getIpMin().getValue() > modificationInfos.getIpMax().getValue()) { + throw new NetworkModificationException(MODIFY_VOLTAGE_LEVEL_ERROR, "IpMin cannot be greater than IpMax"); + } + } else if (ipMinSet || ipMaxSet) { + // only one Icc set: check with existing VL attributes + checkIccValuesAgainstEquipmentInNetwork(network, ipMinSet, ipMaxSet); + } + } + + private void checkIccValuesAgainstEquipmentInNetwork(Network network, boolean ipMinSet, boolean ipMaxSet) { + VoltageLevel existingVoltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getEquipmentId()); + IdentifiableShortCircuit identifiableShortCircuit = existingVoltageLevel.getExtension(IdentifiableShortCircuit.class); + if (Objects.isNull(identifiableShortCircuit)) { + if (ipMinSet) { + throw new NetworkModificationException(MODIFY_VOLTAGE_LEVEL_ERROR, "IpMax is required"); + } + } else { + if (ipMinSet && modificationInfos.getIpMin().getValue() > identifiableShortCircuit.getIpMax() || + ipMaxSet && identifiableShortCircuit.getIpMin() > modificationInfos.getIpMax().getValue()) { + throw new NetworkModificationException(MODIFY_VOLTAGE_LEVEL_ERROR, "IpMin cannot be greater than IpMax"); + } + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + VoltageLevel voltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, modificationInfos.getEquipmentId()); + + subReportNode.newReportNode() + .withMessageTemplate("voltageLevelModification", "Voltage level with id=${id} modified :") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + + ModificationUtils.getInstance().applyElementaryModifications(voltageLevel::setName, () -> voltageLevel.getOptionalName().orElse("No value"), modificationInfos.getEquipmentName(), subReportNode, "Name"); + modifyNominalV(voltageLevel, modificationInfos.getNominalV(), subReportNode); + modifLowVoltageLimit(voltageLevel, modificationInfos.getLowVoltageLimit(), subReportNode); + modifyHighVoltageLimit(voltageLevel, modificationInfos.getHighVoltageLimit(), subReportNode); + + modifyVoltageLevelShortCircuit(modificationInfos.getIpMin(), modificationInfos.getIpMax(), subReportNode, voltageLevel); + PropertiesUtils.applyProperties(voltageLevel, subReportNode, modificationInfos.getProperties(), "VlProperties"); + } + + public static void modifyHighVoltageLimit(VoltageLevel voltageLevel, AttributeModification highVoltageLimit, ReportNode subReportNode) { + ModificationUtils.getInstance().applyElementaryModifications(voltageLevel::setHighVoltageLimit, voltageLevel::getHighVoltageLimit, highVoltageLimit, subReportNode, "High voltage limit"); + } + + public static void modifLowVoltageLimit(VoltageLevel voltageLevel, AttributeModification lowVoltageLimit, ReportNode subReportNode) { + ModificationUtils.getInstance().applyElementaryModifications(voltageLevel::setLowVoltageLimit, voltageLevel::getLowVoltageLimit, lowVoltageLimit, subReportNode, "Low voltage limit"); + } + + public static void modifyNominalV(VoltageLevel voltageLevel, AttributeModification modifNominalV, ReportNode subReportNode) { + ModificationUtils.getInstance().applyElementaryModifications(voltageLevel::setNominalV, voltageLevel::getNominalV, modifNominalV, subReportNode, "Nominal voltage"); + } + + public static void modifyVoltageLevelShortCircuit(AttributeModification ipMin, + AttributeModification ipMax, + ReportNode subReportNode, + VoltageLevel voltageLevel) { + if (ipMin == null && ipMax == null) { + return; + } + + List reports = new ArrayList<>(); + IdentifiableShortCircuitAdder identifiableShortCircuitAdder = voltageLevel.newExtension(IdentifiableShortCircuitAdder.class); + Double oldIpMin = null; + Double oldIpMax = null; + IdentifiableShortCircuit identifiableShortCircuit = voltageLevel.getExtension(IdentifiableShortCircuit.class); + if (identifiableShortCircuit != null) { + oldIpMin = identifiableShortCircuit.getIpMin(); + oldIpMax = identifiableShortCircuit.getIpMax(); + } + + if (ipMin != null) { + var newIpMin = ipMin.getValue(); + + identifiableShortCircuitAdder.withIpMin(newIpMin); + + //convert to kA to report it like the user set it. + var oldIpMinToReport = convertToKiloAmps(oldIpMin); + var newIpMinToReport = convertToKiloAmps(newIpMin); + reports.add(ModificationUtils.getInstance() + .buildModificationReport(oldIpMinToReport, newIpMinToReport, "Low short circuit current limit")); + } else if (oldIpMin != null) { + identifiableShortCircuitAdder.withIpMin(oldIpMin); + } + + if (ipMax != null) { + var newIpMax = ipMax.getValue(); + identifiableShortCircuitAdder.withIpMax(newIpMax); + + //Convert to kA to report it like the user set it. + var oldIpMaxToReport = convertToKiloAmps(oldIpMax); + var newIpMaxToReport = convertToKiloAmps(newIpMax); + reports.add(ModificationUtils.getInstance() + .buildModificationReport(oldIpMaxToReport, newIpMaxToReport, "High short circuit current limit")); + } else if (oldIpMax != null) { + identifiableShortCircuitAdder.withIpMax(oldIpMax); + } + + identifiableShortCircuitAdder.add(); + if (subReportNode != null) { + reports.forEach(report -> insertReportNode(subReportNode, report)); + } + } + + private static Double convertToKiloAmps(Double value) { + return (value != null) ? value * 0.001 : null; + } +} + diff --git a/src/main/java/org/gridsuite/modification/modifications/VscCreation.java b/src/main/java/org/gridsuite/modification/modifications/VscCreation.java new file mode 100644 index 0000000..58afc72 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/VscCreation.java @@ -0,0 +1,284 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControlAdder; +import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRangeAdder; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.ConverterStationCreationInfos; +import org.gridsuite.modification.dto.VscCreationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import java.util.ArrayList; +import java.util.List; + +import static org.gridsuite.modification.NetworkModificationException.Type.*; +import static org.gridsuite.modification.utils.ModificationUtils.createInjectionInNodeBreaker; +import static org.gridsuite.modification.utils.ModificationUtils.reportInjectionCreationConnectivity; + +/** + * @author Seddik Yengui + */ + +public class VscCreation extends AbstractModification { + + public static final String VSC_SETPOINTS = "vscSetPoints"; + public static final String VSC_CHARACTERISTICS = "vscCharacteristics"; + + private final VscCreationInfos modificationInfos; + + public VscCreation(VscCreationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (network.getHvdcLine(modificationInfos.getEquipmentId()) != null) { + throw new NetworkModificationException(HVDC_LINE_ALREADY_EXISTS, modificationInfos.getEquipmentId()); + } + + checkConverterStation(network, modificationInfos.getConverterStation1()); + checkConverterStation(network, modificationInfos.getConverterStation2()); + checkDroop(); + } + + private void checkDroop() { + boolean isPresentAngleDroopActivePowerControl = modificationInfos.getAngleDroopActivePowerControl() != null; + boolean isPresentDroop = modificationInfos.getDroop() != null; + boolean isPresentP0 = modificationInfos.getP0() != null; + // all fields are provided => OK extension will be created + if (isPresentAngleDroopActivePowerControl && isPresentDroop && isPresentP0) { + return; + } + // particular case, not enabling extension and others fields are not provided => OK extension will not be created + if (Boolean.FALSE.equals(modificationInfos.getAngleDroopActivePowerControl()) && !isPresentDroop && !isPresentP0) { + return; + } + // at least one field is provided but not for the others => NOT OK + if (isPresentAngleDroopActivePowerControl || isPresentDroop || isPresentP0) { + throw new NetworkModificationException(WRONG_HVDC_ANGLE_DROOP_ACTIVE_POWER_CONTROL, VscModification.ACTIVE_POWER_CONTROL_DROOP_P0_REQUIRED_ERROR_MSG); + } + // otherwise, i.e. none of the fields is not provided => OK extension will not be created + } + + private void checkConverterStation(Network network, + ConverterStationCreationInfos converterStation) { + if (converterStation == null) { + throw new NetworkModificationException(CREATE_VSC_ERROR, modificationInfos.getEquipmentId() + "Missing required converter station"); + } + // check connectivity + ModificationUtils.getInstance().controlConnectivity(network, + converterStation.getVoltageLevelId(), + converterStation.getBusOrBusbarSectionId(), + converterStation.getConnectionPosition()); + + // check reactive limits + ModificationUtils.getInstance().checkReactiveLimitsCreation(converterStation, + CREATE_VSC_ERROR, + modificationInfos.getEquipmentId(), + "Vsc"); + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + VscConverterStation converterStation1 = createConverterStation(network, modificationInfos.getConverterStation1(), subReportNode, "Converter station 1"); + + VscConverterStation converterStation2 = createConverterStation(network, modificationInfos.getConverterStation2(), subReportNode, "Converter station 2"); + + HvdcLine hvdcLine = network.newHvdcLine() + .setId(modificationInfos.getEquipmentId()) + .setName(modificationInfos.getEquipmentName()) + .setNominalV(modificationInfos.getNominalV()) + .setR(modificationInfos.getR()) + .setMaxP(modificationInfos.getMaxP()) + .setActivePowerSetpoint(modificationInfos.getActivePowerSetpoint()) + .setConvertersMode(modificationInfos.getConvertersMode()) + .setConverterStationId1(converterStation1 != null ? converterStation1.getId() : null) + .setConverterStationId2(converterStation2 != null ? converterStation2.getId() : null) + .add(); + + if (modificationInfos.getOperatorActivePowerLimitFromSide1ToSide2() != null || + modificationInfos.getOperatorActivePowerLimitFromSide2ToSide1() != null) { + hvdcLine.newExtension(HvdcOperatorActivePowerRangeAdder.class) + .withOprFromCS1toCS2(modificationInfos.getOperatorActivePowerLimitFromSide1ToSide2()) + .withOprFromCS2toCS1(modificationInfos.getOperatorActivePowerLimitFromSide2ToSide1()) + .add(); + } + + if (shouldCreateDroopActivePowerControlExtension()) { + var activePowerControlExtension = hvdcLine.newExtension(HvdcAngleDroopActivePowerControlAdder.class) + .withEnabled(modificationInfos.getAngleDroopActivePowerControl()); + activePowerControlExtension.withP0(modificationInfos.getP0()); + activePowerControlExtension.withDroop(modificationInfos.getDroop()); + + activePowerControlExtension.add(); + } + + reportHvdcLineInfos(subReportNode); + + subReportNode.newReportNode() + .withMessageTemplate("vscCreated", "New vsc with id=${id} created") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + + if (modificationInfos.getEquipmentName() != null) { + ModificationUtils.getInstance() + .reportElementaryCreation(subReportNode, modificationInfos.getEquipmentName(), "Name"); + } + + PropertiesUtils.applyProperties(hvdcLine, subReportNode, modificationInfos.getProperties(), "VscProperties"); + } + + private boolean shouldCreateDroopActivePowerControlExtension() { + return VscModification.shouldCreateDroopActivePowerControlExtension( + modificationInfos.getAngleDroopActivePowerControl() != null, modificationInfos.getDroop() != null, modificationInfos.getP0() != null); + } + + private void reportHvdcLineInfos(ReportNode subReportNode) { + List characteristicsReports = new ArrayList<>(); + characteristicsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getNominalV(), "DC nominal voltage")); + characteristicsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getR(), "DC resistance")); + characteristicsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getMaxP(), "Pmax")); + ModificationUtils.getInstance().reportModifications(subReportNode, characteristicsReports, VSC_CHARACTERISTICS, CHARACTERISTICS); + + List limitsReports = new ArrayList<>(); + limitsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getOperatorActivePowerLimitFromSide1ToSide2(), "Operator active power limit (Side1 -> Side 2)")); + limitsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getOperatorActivePowerLimitFromSide2ToSide1(), "Operator active power limit (Side2 -> Side 1)")); + ModificationUtils.getInstance().reportModifications(subReportNode, limitsReports, "vscLimits", "Limits"); + + List setPointsReports = new ArrayList<>(); + setPointsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getConvertersMode(), "Converters mode")); + setPointsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getActivePowerSetpoint(), "Active power")); + ReportNode setPointsReporter = ModificationUtils.getInstance().reportModifications(subReportNode, setPointsReports, VSC_SETPOINTS, SETPOINTS); + + List angleDroopActivePowerControlReports = new ArrayList<>(); + angleDroopActivePowerControlReports.add(ModificationUtils.getInstance() + .createEnabledDisabledReport("angleDroopActivePowerControl", modificationInfos.getAngleDroopActivePowerControl())); + if (modificationInfos.getP0() != null) { + angleDroopActivePowerControlReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getP0(), "P0")); + } + if (modificationInfos.getDroop() != null) { + angleDroopActivePowerControlReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getDroop(), "Droop")); + } + ModificationUtils.getInstance().reportModifications(setPointsReporter, angleDroopActivePowerControlReports, "vscAngleDroop", "Angle droop active power control"); + } + + private VscConverterStation createConverterStation(Network network, + ConverterStationCreationInfos converterStationCreationInfos, + ReportNode subReportNode, + String logFieldName) { + ReportNode converterStationReporter = subReportNode.newReportNode() + .withMessageTemplate("converterStationCreated", "${fieldName} with id=${id} created") + .withUntypedValue("fieldName", logFieldName) + .withUntypedValue("id", converterStationCreationInfos.getEquipmentId()) + .add(); + VoltageLevel voltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, converterStationCreationInfos.getVoltageLevelId()); + VscConverterStation vscConverterStation = voltageLevel.getTopologyKind() == TopologyKind.NODE_BREAKER ? + createConverterStationInNodeBreaker(network, voltageLevel, converterStationCreationInfos, converterStationReporter) : + createConverterStationInBusBreaker(voltageLevel, converterStationCreationInfos, converterStationReporter); + + if (!converterStationCreationInfos.isTerminalConnected()) { + vscConverterStation.getTerminal().disconnect(); + } + + return vscConverterStation; + } + + private VscConverterStation createConverterStationInNodeBreaker(Network network, + VoltageLevel voltageLevel, + ConverterStationCreationInfos converterStationCreationInfos, + ReportNode subReportNode) { + VscConverterStationAdder converterStationAdder = voltageLevel.newVscConverterStation() + .setId(converterStationCreationInfos.getEquipmentId()) + .setName(converterStationCreationInfos.getEquipmentName()) + .setVoltageRegulatorOn(converterStationCreationInfos.getVoltageRegulationOn()); + + if (converterStationCreationInfos.getReactivePowerSetpoint() != null) { + converterStationAdder.setReactivePowerSetpoint(converterStationCreationInfos.getReactivePowerSetpoint()); + } + + if (converterStationCreationInfos.getLossFactor() != null) { + converterStationAdder.setLossFactor(converterStationCreationInfos.getLossFactor()); + } + + if (converterStationCreationInfos.getVoltageSetpoint() != null) { + converterStationAdder.setVoltageSetpoint(converterStationCreationInfos.getVoltageSetpoint()); + } + createInjectionInNodeBreaker(voltageLevel, converterStationCreationInfos, network, converterStationAdder, subReportNode); + VscConverterStation vscConverterStation = ModificationUtils.getInstance() + .getVscConverterStation(network, converterStationCreationInfos.getEquipmentId()); + + addExtensionsAndReports(vscConverterStation, + converterStationCreationInfos, + subReportNode); + + return vscConverterStation; + + } + + private VscConverterStation createConverterStationInBusBreaker(VoltageLevel voltageLevel, + ConverterStationCreationInfos converterStationCreationInfos, + ReportNode subReportNode) { + Bus bus = ModificationUtils.getInstance().getBusBreakerBus(voltageLevel, converterStationCreationInfos.getBusOrBusbarSectionId()); + VscConverterStation vscConverterStation = voltageLevel.newVscConverterStation() + .setId(converterStationCreationInfos.getEquipmentId()) + .setName(converterStationCreationInfos.getEquipmentName()) + .setVoltageRegulatorOn(converterStationCreationInfos.getVoltageRegulationOn()) + .setReactivePowerSetpoint(converterStationCreationInfos.getReactivePowerSetpoint()) + .setBus(bus.getId()) + .setLossFactor(converterStationCreationInfos.getLossFactor()) + .setVoltageSetpoint(converterStationCreationInfos.getVoltageSetpoint()) + .add(); + + addExtensionsAndReports(vscConverterStation, converterStationCreationInfos, subReportNode); + + return vscConverterStation; + } + + private void addExtensionsAndReports(VscConverterStation vscConverterStation, + ConverterStationCreationInfos converterStationCreationInfos, + ReportNode subReporter) { + reportInjectionCreationConnectivity(converterStationCreationInfos, subReporter); + + ModificationUtils.getInstance().reportModifications(subReporter, + List.of(ModificationUtils.getInstance().buildCreationReport(converterStationCreationInfos.getLossFactor(), "Loss Factor")), + "converterStationCharacteristics", + CHARACTERISTICS); + + ModificationUtils.getInstance().createReactiveLimits(converterStationCreationInfos, vscConverterStation, subReporter); + + reportConverterStationSetPoints(converterStationCreationInfos, subReporter); + } + + private void reportConverterStationSetPoints(ConverterStationCreationInfos converterStationCreationInfos, ReportNode subReportNode) { + ReportNode setPointReporter = subReportNode.newReportNode().withMessageTemplate("converterStationSetPoint", SETPOINTS).add(); + + if (converterStationCreationInfos.getReactivePowerSetpoint() != null) { + ModificationUtils.getInstance().reportElementaryCreation(setPointReporter, + converterStationCreationInfos.getReactivePowerSetpoint(), + "Reactive power"); + } + + List setPointsVoltageReports = new ArrayList<>(); + setPointsVoltageReports.add(ModificationUtils.getInstance().createEnabledDisabledReport("voltageRegulationOn", + converterStationCreationInfos.getVoltageRegulationOn())); + if (converterStationCreationInfos.getVoltageSetpoint() != null) { + setPointsVoltageReports.add(ModificationUtils.getInstance().buildCreationReport(converterStationCreationInfos.getReactivePowerSetpoint(), "Voltage")); + } + + ModificationUtils.getInstance().reportModifications(setPointReporter, + setPointsVoltageReports, + "converterStationSetPointsVoltageRegulation", + "Voltage regulation"); + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/VscModification.java b/src/main/java/org/gridsuite/modification/modifications/VscModification.java new file mode 100644 index 0000000..c59a6c3 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/VscModification.java @@ -0,0 +1,362 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControl; +import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControlAdder; +import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRange; +import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRangeAdder; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.ConverterStationModificationInfos; +import org.gridsuite.modification.dto.ReactiveCapabilityCurveModificationInfos; +import org.gridsuite.modification.dto.VscModificationInfos; +import org.gridsuite.modification.utils.ModificationUtils; +import org.gridsuite.modification.utils.PropertiesUtils; + +import javax.annotation.Nonnull; +import java.util.*; + +import static org.gridsuite.modification.NetworkModificationException.Type.MODIFY_VSC_ERROR; +import static org.gridsuite.modification.NetworkModificationException.Type.WRONG_HVDC_ANGLE_DROOP_ACTIVE_POWER_CONTROL; +import static org.gridsuite.modification.modifications.VscCreation.VSC_CHARACTERISTICS; +import static org.gridsuite.modification.modifications.VscCreation.VSC_SETPOINTS; + +/** + * @author jamal kheyyad + */ + +public class VscModification extends AbstractModification { + public static final String NO_VALUE = "No value"; + public static final String ANGLE_DROOP_ACTIVE_POWER_CONTROL_FIELD = "AngleDroopActivePowerControl"; + public static final String DROOP_FIELD = "Droop"; + public static final String P0_FIELD = "P0"; + public static final String ACTIVE_POWER_CONTROL_DROOP_P0_REQUIRED_ERROR_MSG = "Angle droop active power control, Droop and P0 must be all provided or none"; + + private final VscModificationInfos modificationInfos; + + public VscModification(VscModificationInfos vscModificationInfos) { + this.modificationInfos = vscModificationInfos; + } + + public static boolean shouldCreateDroopActivePowerControlExtension(boolean isPresentAngleDroopActivePowerControl, boolean isPresentDroop, boolean isPresentP0) { + return isPresentAngleDroopActivePowerControl && isPresentDroop && isPresentP0; + } + + protected void checkConverterStation(@Nonnull ConverterStationModificationInfos converterStationModificationInfos, @Nonnull VscConverterStation vscConverterStation) { + String errorMessage = "Converter station '" + converterStationModificationInfos.getEquipmentId() + "' : "; + ModificationUtils.getInstance().checkReactiveLimit(vscConverterStation, converterStationModificationInfos.getMinQ(), converterStationModificationInfos.getMaxQ(), + converterStationModificationInfos.getReactiveCapabilityCurvePoints(), MODIFY_VSC_ERROR, errorMessage); + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (modificationInfos == null + || modificationInfos.getConverterStation1() == null + || modificationInfos.getConverterStation2() == null) { + throw new NetworkModificationException(MODIFY_VSC_ERROR, "Missing required attributes to modify the equipment"); + } + HvdcLine hvdcLine = ModificationUtils.getInstance().getHvdcLine(network, modificationInfos.getEquipmentId()); + + VscConverterStation converterStation1 = ModificationUtils.getInstance().getVscConverterStation(network, hvdcLine.getConverterStation1().getId()); + VscConverterStation converterStation2 = ModificationUtils.getInstance().getVscConverterStation(network, hvdcLine.getConverterStation2().getId()); + checkConverterStation(modificationInfos.getConverterStation1(), converterStation1); + checkConverterStation(modificationInfos.getConverterStation2(), converterStation2); + checkDroop(hvdcLine); + } + + private void checkDroop(HvdcLine hvdcLine) { + //--- the extension already exists ---// + HvdcAngleDroopActivePowerControl hvdcAngleDroopActivePowerControl = hvdcLine.getExtension(HvdcAngleDroopActivePowerControl.class); + if (hvdcAngleDroopActivePowerControl != null) { + return; + } + + //--- the extension doesn't exist yet ---// + boolean isPresentAngleDroopActivePowerControl = modificationInfos.getAngleDroopActivePowerControl() != null; + boolean isPresentDroop = modificationInfos.getDroop() != null; + boolean isPresentP0 = modificationInfos.getP0() != null; + // all fields are provided => OK extension will be created + if (isPresentAngleDroopActivePowerControl && isPresentDroop && isPresentP0) { + return; + } + // at least one field is provided but not for the others => NOT OK + if (isPresentAngleDroopActivePowerControl || isPresentDroop || isPresentP0) { + throw new NetworkModificationException(WRONG_HVDC_ANGLE_DROOP_ACTIVE_POWER_CONTROL, ACTIVE_POWER_CONTROL_DROOP_P0_REQUIRED_ERROR_MSG); + } + // otherwise, i.e. none of the fields is provided => OK extension will not be created + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + HvdcLine hvdcLine = ModificationUtils.getInstance().getHvdcLine(network, modificationInfos.getEquipmentId()); + modifyVsc(network, hvdcLine, modificationInfos, subReportNode); + } + + private void modifyVsc(@Nonnull Network network, @Nonnull HvdcLine hvdcLine, VscModificationInfos modificationInfos, ReportNode subReportNode) { + subReportNode.newReportNode() + .withMessageTemplate("VscModification", "Vsc with id=${id} modified :") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + // Characteristics + characteristics(hvdcLine, modificationInfos, subReportNode); + + // Set Points + List setPointsReports = setPoints(hvdcLine); + // Hvdc droop + List droopReports = hvdcAngleDroopActivePowerControlAdder(hvdcLine); + + if (!setPointsReports.isEmpty() || !droopReports.isEmpty()) { + ReportNode setPointsReport = null; + if (!setPointsReports.isEmpty()) { + setPointsReport = ModificationUtils.getInstance().reportModifications(subReportNode, setPointsReports, VSC_SETPOINTS, SETPOINTS); + } + if (!droopReports.isEmpty()) { + if (setPointsReport == null) { + setPointsReport = subReportNode.newReportNode().withMessageTemplate(VSC_SETPOINTS, SETPOINTS).add(); + } + // Hvdc droop logs are in a subReport of Set Points + ModificationUtils.getInstance().reportModifications(setPointsReport, droopReports, "vscAngleDroop", "Angle droop active power control"); + } + } + + // limits + operatorActivePowerLimit(hvdcLine, modificationInfos, subReportNode); + + // stations + modifyConverterStation(ModificationUtils.getInstance().getVscConverterStation(network, hvdcLine.getConverterStation1().getId()), modificationInfos.getConverterStation1(), subReportNode); + modifyConverterStation(ModificationUtils.getInstance().getVscConverterStation(network, hvdcLine.getConverterStation2().getId()), modificationInfos.getConverterStation2(), subReportNode); + + PropertiesUtils.applyProperties(hvdcLine, subReportNode, modificationInfos.getProperties(), "VscProperties"); + } + + private static void characteristics(HvdcLine hvdcLine, VscModificationInfos modificationInfos, ReportNode subReportNode) { + List characteristicsReportsContainer = new ArrayList<>(); + if (modificationInfos.getEquipmentName() != null && modificationInfos.getEquipmentName().getValue() != null) { + characteristicsReportsContainer.add(ModificationUtils.getInstance().applyAndBuildModificationReport(hvdcLine::setName, + () -> hvdcLine.getOptionalName().orElse(NO_VALUE), + modificationInfos.getEquipmentName(), "Name")); + } + if (modificationInfos.getNominalV() != null) { + characteristicsReportsContainer.add(ModificationUtils.getInstance().applyAndBuildModificationReport(hvdcLine::setNominalV, hvdcLine::getNominalV, modificationInfos.getNominalV(), "DC nominal voltage")); + } + if (modificationInfos.getR() != null) { + characteristicsReportsContainer.add(ModificationUtils.getInstance().applyAndBuildModificationReport(hvdcLine::setR, hvdcLine::getR, modificationInfos.getR(), "DC resistance")); + } + if (modificationInfos.getMaxP() != null) { + characteristicsReportsContainer.add(ModificationUtils.getInstance().applyAndBuildModificationReport(hvdcLine::setMaxP, hvdcLine::getMaxP, modificationInfos.getMaxP(), "Power max")); + } + if (!characteristicsReportsContainer.isEmpty()) { + ModificationUtils.getInstance().reportModifications(subReportNode, characteristicsReportsContainer, VSC_CHARACTERISTICS, CHARACTERISTICS); + } + } + + private List setPoints(HvdcLine hvdcLine) { + + List setPointsReports = new ArrayList<>(); + if (modificationInfos.getActivePowerSetpoint() != null) { + setPointsReports.add(ModificationUtils.getInstance().applyAndBuildModificationReport(hvdcLine::setActivePowerSetpoint, hvdcLine::getActivePowerSetpoint, modificationInfos.getActivePowerSetpoint(), "ActivePowerSetpoint")); + } + + if (modificationInfos.getConvertersMode() != null) { + setPointsReports.add(ModificationUtils.getInstance().applyAndBuildModificationReport(hvdcLine::setConvertersMode, hvdcLine::getConvertersMode, modificationInfos.getConvertersMode(), "Converters mode")); + } + return setPointsReports; + } + + private static void operatorActivePowerLimit(HvdcLine hvdcLine, VscModificationInfos modificationInfos, ReportNode subReportNode) { + List reports = new ArrayList<>(); + if (modificationInfos.getOperatorActivePowerLimitFromSide1ToSide2() != null || + modificationInfos.getOperatorActivePowerLimitFromSide2ToSide1() != null) { + var operatorActivePowerRange = hvdcLine.getExtension(HvdcOperatorActivePowerRange.class); + if (operatorActivePowerRange != null) { + modifyOperatorActiveRange(modificationInfos, operatorActivePowerRange, reports); + + } else { + createOperatorActiveRangeExt(hvdcLine, modificationInfos, reports); + } + } + if (!reports.isEmpty()) { + ModificationUtils.getInstance().reportModifications(subReportNode, reports, "vscLimits", "Limits"); + } + } + + private static void modifyOperatorActiveRange(VscModificationInfos modificationInfos, HvdcOperatorActivePowerRange operatorActivePowerRange, List reports) { + var oldCs1ToCs2 = operatorActivePowerRange.getOprFromCS1toCS2(); + var oldCs2ToCs1 = operatorActivePowerRange.getOprFromCS2toCS1(); + Optional.ofNullable(modificationInfos.getOperatorActivePowerLimitFromSide1ToSide2()).ifPresent(info -> { + if (info.getValue() != null) { + operatorActivePowerRange.setOprFromCS1toCS2(info.getValue()); + reports.add(ModificationUtils.getInstance().buildModificationReport(oldCs1ToCs2, info.getValue(), "Operator active power limit (Side1 -> Side 2)")); + } + }); + Optional.ofNullable(modificationInfos.getOperatorActivePowerLimitFromSide2ToSide1()).ifPresent(info -> { + if (info.getValue() != null) { + operatorActivePowerRange.setOprFromCS2toCS1(info.getValue()); + reports.add(ModificationUtils.getInstance().buildModificationReport(oldCs2ToCs1, info.getValue(), "Operator active power limit (Side2 -> Side 1)")); + } + }); + } + + private static void createOperatorActiveRangeExt(HvdcLine hvdcLine, VscModificationInfos modificationInfos, List reports) { + var hvdcOperatorActivePowerRangeAddr = hvdcLine.newExtension(HvdcOperatorActivePowerRangeAdder.class); + Optional.ofNullable(modificationInfos.getOperatorActivePowerLimitFromSide1ToSide2()).ifPresent(info -> { + if (info.getValue() != null) { + hvdcOperatorActivePowerRangeAddr.withOprFromCS1toCS2(modificationInfos.getOperatorActivePowerLimitFromSide1ToSide2().getValue()); + reports.add(ModificationUtils.getInstance().buildModificationReport(null, info.getValue(), "Operator active power limit (Side1 -> Side 2)")); + } + }); + Optional.ofNullable(modificationInfos.getOperatorActivePowerLimitFromSide2ToSide1()).ifPresent(info -> { + if (info.getValue() != null) { + hvdcOperatorActivePowerRangeAddr.withOprFromCS2toCS1(modificationInfos.getOperatorActivePowerLimitFromSide2ToSide1().getValue()); + reports.add(ModificationUtils.getInstance().buildModificationReport(null, info.getValue(), "Operator active power limit (Side2 -> Side 1)")); + } + }); + hvdcOperatorActivePowerRangeAddr.add(); + } + + private void modifyExistingHvdcAngleDroopActivePowerControl(HvdcAngleDroopActivePowerControl hvdcAngleDroopActivePowerControl, List reports) { + Optional.ofNullable(modificationInfos.getAngleDroopActivePowerControl()).ifPresent(modification -> + reports.add(ModificationUtils.getInstance().applyAndBuildModificationReport( + hvdcAngleDroopActivePowerControl::setEnabled, + hvdcAngleDroopActivePowerControl::isEnabled, + modification, + ANGLE_DROOP_ACTIVE_POWER_CONTROL_FIELD))); + + Optional.ofNullable(modificationInfos.getDroop()).ifPresent(modification -> + reports.add(ModificationUtils.getInstance().applyAndBuildModificationReport( + hvdcAngleDroopActivePowerControl::setDroop, + hvdcAngleDroopActivePowerControl::getDroop, + modification, + DROOP_FIELD))); + + Optional.ofNullable(modificationInfos.getP0()).ifPresent(modification -> + reports.add(ModificationUtils.getInstance().applyAndBuildModificationReport( + hvdcAngleDroopActivePowerControl::setP0, + hvdcAngleDroopActivePowerControl::getP0, + modification, + P0_FIELD))); + } + + private boolean shouldCreateDroopActivePowerControlExtension() { + return shouldCreateDroopActivePowerControlExtension( + modificationInfos.getAngleDroopActivePowerControl() != null, modificationInfos.getDroop() != null, modificationInfos.getP0() != null); + } + + private List hvdcAngleDroopActivePowerControlAdder(HvdcLine hvdcLine) { + List reports = new ArrayList<>(); + var hvdcAngleDroopActivePowerControl = hvdcLine.getExtension(HvdcAngleDroopActivePowerControl.class); + if (hvdcAngleDroopActivePowerControl != null) { + modifyExistingHvdcAngleDroopActivePowerControl(hvdcAngleDroopActivePowerControl, reports); + } else if (shouldCreateDroopActivePowerControlExtension()) { + HvdcAngleDroopActivePowerControlAdder hvdcAngleDroopActivePowerControlAdder = + hvdcLine.newExtension(HvdcAngleDroopActivePowerControlAdder.class); + + Boolean isEnabled = modificationInfos.getAngleDroopActivePowerControl().getValue(); + hvdcAngleDroopActivePowerControlAdder.withEnabled(isEnabled); + reports.add(ModificationUtils.getInstance().buildModificationReport(null, isEnabled, ANGLE_DROOP_ACTIVE_POWER_CONTROL_FIELD)); + + Float droop = modificationInfos.getDroop().getValue(); + hvdcAngleDroopActivePowerControlAdder.withDroop(droop); + reports.add(ModificationUtils.getInstance().buildModificationReport(Float.NaN, droop, DROOP_FIELD)); + + Float p0 = modificationInfos.getP0().getValue(); + hvdcAngleDroopActivePowerControlAdder.withP0(p0); + reports.add(ModificationUtils.getInstance().buildModificationReport(Float.NaN, p0, P0_FIELD)); + + hvdcAngleDroopActivePowerControlAdder.add(); + } + return reports; + } + + private void modifyConverterStation(VscConverterStation converterStation, ConverterStationModificationInfos converterStationModificationInfos, ReportNode subReportNode) { + if (converterStationModificationInfos == null || !isConverterStationModified(converterStationModificationInfos)) { + return; + } + + ReportNode converterStationReportNode = subReportNode.newReportNode() + .withMessageTemplate("Converter Station", "Converter station ${id} modified") + .withUntypedValue("id", converterStation.getId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + + // characteristic + List characteristicReports = new ArrayList<>(); + if (converterStationModificationInfos.getEquipmentName() != null && converterStationModificationInfos.getEquipmentName().getValue() != null) { + characteristicReports.add(ModificationUtils.getInstance().applyAndBuildModificationReport(converterStation::setName, + () -> converterStation.getOptionalName().orElse(NO_VALUE), converterStationModificationInfos.getEquipmentName(), "Name")); + } + + if (converterStationModificationInfos.getLossFactor() != null) { + characteristicReports.add(ModificationUtils.getInstance().applyAndBuildModificationReport(converterStation::setLossFactor, + converterStation::getLossFactor, converterStationModificationInfos.getLossFactor(), "LossFactor")); + } + + if (!characteristicReports.isEmpty()) { + ModificationUtils.getInstance().reportModifications(converterStationReportNode, + characteristicReports, "Characteristics", "Characteristics"); + } + + // set points + List setPointsReports = new ArrayList<>(); + if (converterStationModificationInfos.getReactivePowerSetpoint() != null) { + setPointsReports.add(ModificationUtils.getInstance().applyAndBuildModificationReport(converterStation::setReactivePowerSetpoint, + converterStation::getReactivePowerSetpoint, converterStationModificationInfos.getReactivePowerSetpoint(), "Reactive Power")); + } + + if (converterStationModificationInfos.getVoltageRegulationOn() != null) { + setPointsReports.add(ModificationUtils.getInstance().applyAndBuildModificationReport(converterStation::setVoltageRegulatorOn, + converterStation::isVoltageRegulatorOn, converterStationModificationInfos.getVoltageRegulationOn(), "VoltageRegulationOn")); + } + + if (converterStationModificationInfos.getVoltageSetpoint() != null) { + setPointsReports.add(ModificationUtils.getInstance().applyAndBuildModificationReport(converterStation::setVoltageSetpoint, + converterStation::getVoltageSetpoint, converterStationModificationInfos.getVoltageSetpoint(), "Voltage")); + } + if (!setPointsReports.isEmpty()) { + ModificationUtils.getInstance().reportModifications(converterStationReportNode, + setPointsReports, SETPOINTS, SETPOINTS); + } + + // limits + modifyVscReactiveLimitsAttributes(converterStationModificationInfos, converterStation, converterStationReportNode, converterStationReportNode); + } + + private static boolean isConverterStationModified(ConverterStationModificationInfos converterStationModificationInfos) { + return converterStationModificationInfos.getEquipmentName() != null && converterStationModificationInfos.getEquipmentName().getValue() != null || converterStationModificationInfos.getLossFactor() != null + || converterStationModificationInfos.getReactivePowerSetpoint() != null + || converterStationModificationInfos.getVoltageRegulationOn() != null + || converterStationModificationInfos.getVoltageSetpoint() != null || converterStationModificationInfos.getReactiveCapabilityCurvePoints() != null + || converterStationModificationInfos.getMinQ() != null || converterStationModificationInfos.getMaxQ() != null; + } + + private void modifyVscReactiveCapabilityCurvePoints(ConverterStationModificationInfos modificationInfos, + VscConverterStation vscConverterStation, ReportNode subReporter, ReportNode subReportNodeLimits) { + + ReactiveCapabilityCurveAdder adder = vscConverterStation.newReactiveCapabilityCurve(); + List modificationPoints = modificationInfos.getReactiveCapabilityCurvePoints(); + Collection points = vscConverterStation.getReactiveLimits().getKind() == ReactiveLimitsKind.CURVE ? vscConverterStation.getReactiveLimits(ReactiveCapabilityCurve.class).getPoints() : List.of(); + ModificationUtils.getInstance().modifyReactiveCapabilityCurvePoints(points, modificationPoints, adder, subReporter, subReportNodeLimits); + } + + private void modifyVscReactiveLimitsAttributes(ConverterStationModificationInfos modificationInfos, + VscConverterStation vscConverterStation, ReportNode subReportNode, ReportNode subReportNodeLimits) { + + if (modificationInfos.getReactiveCapabilityCurve() != null) { + if (Boolean.TRUE.equals(modificationInfos.getReactiveCapabilityCurve().getValue() + && modificationInfos.getReactiveCapabilityCurvePoints() != null + && !modificationInfos.getReactiveCapabilityCurvePoints().isEmpty())) { + modifyVscReactiveCapabilityCurvePoints(modificationInfos, vscConverterStation, subReportNode, subReportNodeLimits); + } else if (Boolean.FALSE.equals(modificationInfos.getReactiveCapabilityCurve().getValue())) { + ModificationUtils.getInstance().modifyMinMaxReactiveLimits(modificationInfos.getMinQ(), modificationInfos.getMaxQ(), vscConverterStation, subReportNode, subReportNodeLimits); + } + } + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java new file mode 100644 index 0000000..f9992c4 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java @@ -0,0 +1,366 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.modifications.byfilter; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.IdentifiableType; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import org.apache.commons.lang3.StringUtils; +import org.gridsuite.modification.IFilterService; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.FilterEquipments; +import org.gridsuite.modification.dto.FilterInfos; +import org.gridsuite.modification.dto.ModificationInfos; +import org.gridsuite.modification.dto.byfilter.AbstractAssignmentInfos; +import org.gridsuite.modification.dto.byfilter.equipmentfield.TwoWindingsTransformerField; +import org.gridsuite.modification.modifications.AbstractModification; +import org.gridsuite.modification.utils.ModificationUtils; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import static org.gridsuite.modification.dto.byfilter.equipmentfield.FieldUtils.getFieldValue; +import static org.gridsuite.modification.dto.byfilter.equipmentfield.FieldUtils.setFieldValue; +import static org.gridsuite.modification.utils.ModificationUtils.*; + +/** + * @author Thang PHAM + */ +public abstract class AbstractModificationByAssignment extends AbstractModification { + public static final String VALUE_KEY_FILTER_NAME = "filterName"; + public static final String VALUE_KEY_FIELD_NAME = "fieldName"; + public static final String VALUE_KEY_EQUIPMENT_NAME = "equipmentName"; + public static final String VALUE_KEY_EQUIPMENT_TYPE = "equipmentType"; + public static final String VALUE_KEY_EQUIPMENT_COUNT = "equipmentCount"; + public static final String VALUE_KEY_EQUIPMENT_IDS = "equipmentIds"; + public static final String VALUE_KEY_NB_CHANGED = "nbChanged"; + public static final String VALUE_KEY_NB_UNCHANGED = "nbUnchanged"; + public static final String VALUE_KEY_OLD_VALUE = "oldValue"; + public static final String VALUE_KEY_NEW_VALUE = "newValue"; + public static final String VALUE_KEY_MODIFICATION_TYPE_LABEL = "modificationTypeLabel"; + public static final String VALUE_KEY_FILTERS_EACH_ASSIGNMENT = "filtersEachAssignment"; + public static final String VALUE_KEY_ERROR_MESSAGE = "errorMessage"; + public static final String REPORT_KEY_RATIO_TAP_CHANGER_EQUIPMENT_MODIFIED_ERROR = "ratioTapChangerEquipmentModifiedError"; + public static final String REPORT_KEY_PHASE_TAP_CHANGER_EQUIPMENT_MODIFIED_ERROR = "phaseTapChangerEquipmentModifiedError"; + public static final String REPORT_KEY_EQUIPMENT_MODIFIED_ERROR = "equipmentModifiedError"; + public static final String REPORT_KEY_BY_FILTER_MODIFICATION_SOME = "byFilterModificationSome"; + public static final String REPORT_KEY_BY_FILTER_MODIFICATION_FAILED = "byFilterModificationFailed"; + public static final String REPORT_KEY_BY_FILTER_MODIFICATION_SUCCESS = "byFilterModificationSuccess"; + public static final String REPORT_KEY_NUMBER_OF_VALID_EQUIPMENT = "numberOfValidEquipment"; + public static final String REPORT_KEY_NOT_EDITED_EQUIPMENTS_FILTER = "notEditedEquipmentsFilter"; + public static final String REPORT_KEY_EDITED_FIELD_FILTER = "editedFieldFilter"; + public static final String REPORT_KEY_FILTER_EQUIPMENTS_NOT_FOUND = "filterEquipmentsNotFound"; + public static final String REPORT_KEY_EQUIPMENT_MODIFIED_REPORT = "equipmentModifiedReport"; + public static final String REPORT_KEY_EQUIPMENT_MODIFIED_REPORT_EXCEPTION = "equipmentModifiedReportException"; + public static final String REPORT_KEY_APPLIED_BY_FILTER_MODIFICATIONS = "appliedByFilterModifications"; + public static final String REPORT_KEY_APPLIED_ASSIGNMENT = "appliedAssignment"; + public static final String REPORT_KEY_BY_FILTER_MODIFICATION_ALL = "byFilterModificationAll"; + public static final String REPORT_KEY_BY_FILTER_MODIFICATION_NONE = "byFilterModificationNone"; + public static final String REPORT_KEY_BY_FILTER_MODIFICATION_NOT_FOUND = "byFilterModificationNotFound"; + + protected IFilterService filterService; + protected int equipmentNotModifiedCount; + protected long equipmentCount; + protected long equipmentNotFoundCount; + + protected AbstractModificationByAssignment() { + equipmentNotModifiedCount = 0; + equipmentCount = 0; + equipmentNotFoundCount = 0; + } + + public abstract String getModificationTypeLabel(); + + private String getEditedFieldLabel(AbstractAssignmentInfos modificationByFilterInfos) { + return modificationByFilterInfos.getEditedFieldLabel(); + } + + public abstract ModificationInfos getModificationInfos(); + + public abstract IdentifiableType getEquipmentType(); + + public abstract NetworkModificationException.Type getExceptionType(); + + public abstract List getAssignmentInfosList(); + + protected abstract boolean preCheckValue(Identifiable equipment, + AbstractAssignmentInfos abstractAssignmentInfos, + List reports, List notEditableEquipments); + + protected abstract String getNewValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos); + + protected String getOldValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos) { + return getFieldValue(equipment, abstractAssignmentInfos.getEditedField()); + } + + protected String applyValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos) { + // get new value + String newValue = getNewValue(equipment, abstractAssignmentInfos); + + // set new value for the equipment + setFieldValue(equipment, abstractAssignmentInfos.getEditedField(), newValue); + return newValue; + } + + @Override + public void initApplicationContext(IFilterService filterService) { + this.filterService = filterService; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (getModificationInfos() == null) { + throw new NetworkModificationException(getExceptionType(), "Missing required attributes to modify the equipment"); + } + + if (CollectionUtils.isEmpty(getAssignmentInfosList())) { + throw new NetworkModificationException(getExceptionType(), String.format("At least one %s is required", getModificationTypeLabel())); + } + + if (getAssignmentInfosList().stream().anyMatch(modificationByFilterInfos -> CollectionUtils.isEmpty(modificationByFilterInfos.getFilters()))) { + throw new NetworkModificationException(getExceptionType(), String.format("Every %s must have at least one filter", getModificationTypeLabel())); + } + } + + @Override + public void apply(Network network, ReportNode subReportNode) { + // collect all filters from all variations + Map filters = getFilters(); + + Map filterUuidEquipmentsMap = ModificationUtils.getUuidFilterEquipmentsMap(filterService, network, subReportNode, filters, getModificationInfos().getErrorType()); + + if (filterUuidEquipmentsMap != null) { + ReportNode subReporter = subReportNode.newReportNode() + .withMessageTemplate(REPORT_KEY_APPLIED_BY_FILTER_MODIFICATIONS, "${" + VALUE_KEY_MODIFICATION_TYPE_LABEL + "}s on ${" + VALUE_KEY_EQUIPMENT_TYPE + "} type") + .withUntypedValue(VALUE_KEY_MODIFICATION_TYPE_LABEL, StringUtils.capitalize(getModificationTypeLabel())) + .withUntypedValue(VALUE_KEY_EQUIPMENT_TYPE, getEquipmentType().name()) + .add(); + // perform modifications + getAssignmentInfosList().forEach(abstractAssignmentInfos -> { + List reports = new ArrayList<>(); + ReportNode eachAssignmentReporter = subReporter.newReportNode() + .withMessageTemplate(REPORT_KEY_APPLIED_ASSIGNMENT, "${" + VALUE_KEY_MODIFICATION_TYPE_LABEL + "} on filters : ${" + VALUE_KEY_FILTERS_EACH_ASSIGNMENT + "}") + .withUntypedValue(VALUE_KEY_MODIFICATION_TYPE_LABEL, StringUtils.capitalize(getModificationTypeLabel())) + .withUntypedValue(VALUE_KEY_FILTERS_EACH_ASSIGNMENT, abstractAssignmentInfos.getFilters().stream().map(FilterInfos::getName) + .collect(Collectors.joining(", "))) + .add(); + abstractAssignmentInfos.getFilters().forEach(filterInfos -> applyOnFilterEquipments(network, filterUuidEquipmentsMap, reports, abstractAssignmentInfos, filterInfos)); + reports.forEach(report -> insertReportNode(eachAssignmentReporter, report)); + }); + // reporting + if (equipmentNotModifiedCount == 0 && equipmentNotFoundCount == 0) { + subReportNode.newReportNode() + .withMessageTemplate(REPORT_KEY_BY_FILTER_MODIFICATION_ALL, + "All equipments have been modified : ${" + VALUE_KEY_EQUIPMENT_COUNT + "} equipment(s)") + .withUntypedValue(VALUE_KEY_EQUIPMENT_COUNT, equipmentCount) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + + } else { + if (equipmentNotModifiedCount == equipmentCount) { + createReport(subReportNode, REPORT_KEY_BY_FILTER_MODIFICATION_NONE, + "No equipment have been modified", + Map.of(), TypedValue.ERROR_SEVERITY); + } else { + subReportNode.newReportNode() + .withMessageTemplate(REPORT_KEY_BY_FILTER_MODIFICATION_SOME, + "Some of the equipment have been modified : ${" + VALUE_KEY_NB_CHANGED + "} equipment(s) modified and ${" + VALUE_KEY_NB_UNCHANGED + "} equipment(s) not modified") + .withUntypedValue(VALUE_KEY_NB_CHANGED, equipmentCount - equipmentNotModifiedCount) + .withUntypedValue(VALUE_KEY_NB_UNCHANGED, equipmentNotModifiedCount + equipmentNotFoundCount) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + } + } + } + } + + protected boolean isEquipmentEditable(Identifiable equipment, + AbstractAssignmentInfos abstractAssignmentInfos, + List equipmentsReport) { + if (abstractAssignmentInfos.getEditedField() == null) { + return false; + } + + if (equipment.getType() == IdentifiableType.TWO_WINDINGS_TRANSFORMER) { + TwoWindingsTransformerField editedField = TwoWindingsTransformerField.valueOf(abstractAssignmentInfos.getEditedField()); + TwoWindingsTransformer twoWindingsTransformer = (TwoWindingsTransformer) equipment; + return switch (editedField) { + case TARGET_V, RATIO_LOW_TAP_POSITION, RATIO_TAP_POSITION, RATIO_TARGET_DEADBAND -> { + boolean isEditable = twoWindingsTransformer.getRatioTapChanger() != null; + if (!isEditable) { + equipmentsReport.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_RATIO_TAP_CHANGER_EQUIPMENT_MODIFIED_ERROR + equipmentsReport.size(), + " Cannot modify field ${" + VALUE_KEY_FIELD_NAME + "} of equipment ${" + VALUE_KEY_EQUIPMENT_NAME + "} : Ratio tab changer is null") + .withUntypedValue(VALUE_KEY_FIELD_NAME, editedField.name()) + .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, equipment.getId()) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + } + yield isEditable; + } + case REGULATION_VALUE, PHASE_LOW_TAP_POSITION, PHASE_TAP_POSITION, PHASE_TARGET_DEADBAND -> { + boolean isEditable = twoWindingsTransformer.getPhaseTapChanger() != null; + if (!isEditable) { + equipmentsReport.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_PHASE_TAP_CHANGER_EQUIPMENT_MODIFIED_ERROR + equipmentsReport.size(), + " Cannot modify field ${" + VALUE_KEY_FIELD_NAME + "} of equipment ${" + VALUE_KEY_EQUIPMENT_NAME + "} : Phase tab changer is null") + .withUntypedValue(VALUE_KEY_FIELD_NAME, editedField.name()) + .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, equipment.getId()) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + } + yield isEditable; + } + default -> true; + }; + } + return true; + } + + private void createAssignmentReports(List reports, AbstractAssignmentInfos abstractAssignmentInfos, + FilterInfos filterInfos, FilterEquipments filterEquipments, List notEditableEquipments) { + if (notEditableEquipments.size() == filterEquipments.getIdentifiableAttributes().size()) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_BY_FILTER_MODIFICATION_FAILED, + "No equipment(s) have been modified on filter ${" + VALUE_KEY_FILTER_NAME + "}") + .withUntypedValue(VALUE_KEY_FILTER_NAME, filterInfos.getName()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_BY_FILTER_MODIFICATION_SUCCESS, + "Successful application of ${" + VALUE_KEY_MODIFICATION_TYPE_LABEL + "} on filter ${" + VALUE_KEY_FILTER_NAME + "}") + .withUntypedValue(VALUE_KEY_MODIFICATION_TYPE_LABEL, getModificationTypeLabel()) + .withUntypedValue(VALUE_KEY_FILTER_NAME, filterInfos.getName()) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_NUMBER_OF_VALID_EQUIPMENT, " Number of equipment modified : ${" + VALUE_KEY_NB_CHANGED + "}") + .withUntypedValue(VALUE_KEY_NB_CHANGED, filterEquipments.getIdentifiableAttributes().size() - notEditableEquipments.size()) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + + if (!CollectionUtils.isEmpty(notEditableEquipments)) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_NOT_EDITED_EQUIPMENTS_FILTER, + " ${" + VALUE_KEY_NB_UNCHANGED + "} equipment(s) were not modified") + .withUntypedValue(VALUE_KEY_NB_UNCHANGED, notEditableEquipments.size()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } + } + + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_EDITED_FIELD_FILTER, " Edited field : ${" + VALUE_KEY_FIELD_NAME + "}") + .withUntypedValue(VALUE_KEY_FIELD_NAME, getEditedFieldLabel(abstractAssignmentInfos)) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + + if (!CollectionUtils.isEmpty(filterEquipments.getNotFoundEquipments())) { + String equipmentIds = String.join(", ", filterEquipments.getNotFoundEquipments()); + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_FILTER_EQUIPMENTS_NOT_FOUND, " Equipment not found : ${" + VALUE_KEY_EQUIPMENT_IDS + "}") + .withUntypedValue(VALUE_KEY_EQUIPMENT_IDS, equipmentIds) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } + } + + private void applyModification(Identifiable equipment, + AbstractAssignmentInfos abstractAssignmentInfos, + List reports, + List notEditableEquipments) { + + // check pre-conditions + if (!preCheckValue(equipment, abstractAssignmentInfos, reports, notEditableEquipments)) { + return; + } + + // perform to apply new value + try { + final String oldValue = getOldValue(equipment, abstractAssignmentInfos); + final String newValue = applyValue(equipment, abstractAssignmentInfos); + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_EQUIPMENT_MODIFIED_REPORT, + " ${" + VALUE_KEY_EQUIPMENT_TYPE + "} id : ${" + VALUE_KEY_EQUIPMENT_NAME + + "}, ${" + VALUE_KEY_FIELD_NAME + "} : ${" + VALUE_KEY_OLD_VALUE + "} → ${" + VALUE_KEY_NEW_VALUE + "}") + .withUntypedValue(VALUE_KEY_EQUIPMENT_TYPE, equipment.getType().name()) + .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, equipment.getId()) + .withUntypedValue(VALUE_KEY_FIELD_NAME, getEditedFieldLabel(abstractAssignmentInfos)) + .withUntypedValue(VALUE_KEY_OLD_VALUE, oldValue == null ? NO_VALUE : oldValue) + .withUntypedValue(VALUE_KEY_NEW_VALUE, newValue) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + } catch (Exception e) { + notEditableEquipments.add(equipment.getId()); + equipmentNotModifiedCount += 1; + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_EQUIPMENT_MODIFIED_REPORT_EXCEPTION, + " Cannot modify equipment ${" + VALUE_KEY_EQUIPMENT_NAME + "} : ${" + VALUE_KEY_ERROR_MESSAGE + "}") + .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, equipment.getId()) + .withUntypedValue(VALUE_KEY_ERROR_MESSAGE, e.getMessage()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } + } + + private Map getFilters() { + return getAssignmentInfosList().stream() + .flatMap(v -> v.getFilters().stream()) + .filter(distinctByKey(FilterInfos::getId)) + .collect(Collectors.toMap(FilterInfos::getId, FilterInfos::getName)); + } + + private void applyOnFilterEquipments(Network network, + Map filterUuidEquipmentsMap, + List reports, + AbstractAssignmentInfos abstractAssignmentInfos, + FilterInfos filterInfos) { + FilterEquipments filterEquipments = filterUuidEquipmentsMap.get(filterInfos.getId()); + + if (CollectionUtils.isEmpty(filterEquipments.getIdentifiableAttributes())) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_BY_FILTER_MODIFICATION_NOT_FOUND, "No equipments were found for filter ${" + VALUE_KEY_FILTER_NAME + "}") + .withUntypedValue(VALUE_KEY_FILTER_NAME, filterInfos.getName()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else { + equipmentCount += filterEquipments.getIdentifiableAttributes().size(); + if (!CollectionUtils.isEmpty(filterEquipments.getNotFoundEquipments())) { + equipmentNotFoundCount += filterEquipments.getNotFoundEquipments().size(); + } + List notEditableEquipments = new ArrayList<>(); + List equipmentsReport = new ArrayList<>(); + filterEquipments.getIdentifiableAttributes() + .stream() + .map(attributes -> network.getIdentifiable(attributes.getId())) + .filter(equipment -> { + boolean isEditableEquipment = isEquipmentEditable(equipment, abstractAssignmentInfos, equipmentsReport); + if (!isEditableEquipment) { + notEditableEquipments.add(equipment.getId()); + equipmentNotModifiedCount += 1; + } + return isEditableEquipment; + }) + .forEach(equipment -> applyModification(equipment, abstractAssignmentInfos, equipmentsReport, notEditableEquipments)); + + createAssignmentReports(reports, abstractAssignmentInfos, filterInfos, filterEquipments, notEditableEquipments); + + reports.addAll(equipmentsReport); + } + } + +} diff --git a/src/main/java/org/gridsuite/modification/modifications/byfilter/ByFormulaModification.java b/src/main/java/org/gridsuite/modification/modifications/byfilter/ByFormulaModification.java new file mode 100644 index 0000000..c9bab0e --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/byfilter/ByFormulaModification.java @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.modifications.byfilter; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.IdentifiableType; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.ByFormulaModificationInfos; +import org.gridsuite.modification.dto.ModificationInfos; +import org.gridsuite.modification.dto.byfilter.AbstractAssignmentInfos; +import org.gridsuite.modification.dto.byfilter.formula.FormulaInfos; +import org.gridsuite.modification.dto.byfilter.formula.Operator; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.List; + +import static org.gridsuite.modification.NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR; + +/** + * @author Thang PHAM + */ +public class ByFormulaModification extends AbstractModificationByAssignment { + private final ByFormulaModificationInfos modificationInfos; + + public ByFormulaModification(ByFormulaModificationInfos modificationInfos) { + super(); + this.modificationInfos = modificationInfos; + } + + @Override + public String getModificationTypeLabel() { + return "formula"; + } + + @Override + public ModificationInfos getModificationInfos() { + return modificationInfos; + } + + @Override + public IdentifiableType getEquipmentType() { + return modificationInfos.getIdentifiableType(); + } + + @Override + public List getAssignmentInfosList() { + return Collections.unmodifiableList(modificationInfos.getFormulaInfosList()); + } + + @Override + public NetworkModificationException.Type getExceptionType() { + return BY_FORMULA_MODIFICATION_ERROR; + } + + @Override + protected boolean preCheckValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos, List reports, List notEditableEquipments) { + FormulaInfos formulaInfos = (FormulaInfos) abstractAssignmentInfos; + Double value1 = formulaInfos.getFieldOrValue1().getRefOrValue(equipment); + Double value2 = formulaInfos.getFieldOrValue2().getRefOrValue(equipment); + if (value1 == null || Double.isNaN(value1) || value2 == null || Double.isNaN(value2)) { + equipmentNotModifiedCount += 1; + notEditableEquipments.add(equipment.getId()); + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_EQUIPMENT_MODIFIED_ERROR, " Cannot modify equipment ${" + + VALUE_KEY_EQUIPMENT_NAME + "} : At least one of the value or referenced field is null") + .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, equipment.getId()) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + return false; + } + + if (value2 == 0 && formulaInfos.getOperator() == Operator.DIVISION) { + equipmentNotModifiedCount += 1; + notEditableEquipments.add(equipment.getId()); + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate(REPORT_KEY_EQUIPMENT_MODIFIED_ERROR, " Cannot modify equipment ${" + + VALUE_KEY_EQUIPMENT_NAME + "} : The value or referenced field of the second operand in the division operator is zero") + .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, equipment.getId()) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + return false; + } + return true; + } + + @Override + protected String getNewValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos) { + FormulaInfos formulaInfos = (FormulaInfos) abstractAssignmentInfos; + Double value1 = formulaInfos.getFieldOrValue1().getRefOrValue(equipment); + Double value2 = formulaInfos.getFieldOrValue2().getRefOrValue(equipment); + return applyOperation(formulaInfos.getOperator(), value1, value2).toString(); + } + + private Double applyOperation(Operator operator, @Nonnull Double value1, @Nonnull Double value2) { + return switch (operator) { + case ADDITION -> value1 + value2; + case SUBTRACTION -> value1 - value2; + case MULTIPLICATION -> value1 * value2; + case DIVISION -> value1 / value2; + case PERCENTAGE -> (value1 / 100) * value2; + }; + } +} diff --git a/src/main/java/org/gridsuite/modification/modifications/byfilter/ModificationByAssignment.java b/src/main/java/org/gridsuite/modification/modifications/byfilter/ModificationByAssignment.java new file mode 100644 index 0000000..a8be2e7 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/modifications/byfilter/ModificationByAssignment.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.modifications.byfilter; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.IdentifiableType; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.ModificationByAssignmentInfos; +import org.gridsuite.modification.dto.ModificationInfos; +import org.gridsuite.modification.dto.byfilter.AbstractAssignmentInfos; +import org.gridsuite.modification.dto.byfilter.DataType; +import org.gridsuite.modification.dto.byfilter.assignment.AssignmentInfos; +import org.gridsuite.modification.dto.byfilter.assignment.PropertyAssignmentInfos; + +import java.util.Collections; +import java.util.List; + +import static org.gridsuite.modification.NetworkModificationException.Type.MODIFICATION_BY_ASSIGNMENT_ERROR; + +/** + * @author Thang PHAM + */ +public class ModificationByAssignment extends AbstractModificationByAssignment { + private final ModificationByAssignmentInfos modificationInfos; + + public ModificationByAssignment(ModificationByAssignmentInfos modificationInfos) { + super(); + this.modificationInfos = modificationInfos; + } + + @Override + public String getModificationTypeLabel() { + return "assignment"; + } + + @Override + public ModificationInfos getModificationInfos() { + return modificationInfos; + } + + @Override + public IdentifiableType getEquipmentType() { + return modificationInfos.getEquipmentType(); + } + + @Override + public List getAssignmentInfosList() { + return Collections.unmodifiableList(modificationInfos.getAssignmentInfosList()); + } + + @Override + public NetworkModificationException.Type getExceptionType() { + return MODIFICATION_BY_ASSIGNMENT_ERROR; + } + + @Override + protected boolean isEquipmentEditable(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos, List equipmentsReport) { + AssignmentInfos assignmentInfos = (AssignmentInfos) abstractAssignmentInfos; + if (assignmentInfos.getDataType() == DataType.PROPERTY) { + return true; + } else { + return super.isEquipmentEditable(equipment, abstractAssignmentInfos, equipmentsReport); + } + } + + @Override + protected boolean preCheckValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos, List reports, List notEditableEquipments) { + return true; + } + + @Override + protected String getOldValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos) { + AssignmentInfos assignmentInfos = (AssignmentInfos) abstractAssignmentInfos; + if (assignmentInfos.getDataType() == DataType.PROPERTY) { + return equipment.getProperty(((PropertyAssignmentInfos) assignmentInfos).getPropertyName()); + } else { + return super.getOldValue(equipment, abstractAssignmentInfos); + } + } + + @Override + protected String getNewValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos) { + AssignmentInfos assignmentInfos = (AssignmentInfos) abstractAssignmentInfos; + if (assignmentInfos.getValue() == null) { + throw new NetworkModificationException(MODIFICATION_BY_ASSIGNMENT_ERROR, "Missing a value in the assignment"); + } + return assignmentInfos.getValue().toString(); + } + + @Override + protected String applyValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos) { + AssignmentInfos assignmentInfos = (AssignmentInfos) abstractAssignmentInfos; + if (assignmentInfos.getDataType() == DataType.PROPERTY) { + String newValue = getNewValue(equipment, abstractAssignmentInfos); + equipment.setProperty(((PropertyAssignmentInfos) assignmentInfos).getPropertyName(), newValue); + return newValue; + } else { + return super.applyValue(equipment, abstractAssignmentInfos); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java new file mode 100644 index 0000000..a4c049c --- /dev/null +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -0,0 +1,1572 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.utils; + +import com.powsybl.commons.report.ReportConstants; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.ReportNodeAdder; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.modification.topology.*; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.*; +import com.powsybl.math.graph.TraversalType; +import com.powsybl.network.store.iidm.impl.MinMaxReactiveLimitsImpl; + +import org.gridsuite.modification.IFilterService; +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.*; +import org.gridsuite.modification.modifications.BusbarSectionFinderTraverser; +import org.jetbrains.annotations.Nullable; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static org.gridsuite.modification.NetworkModificationException.Type.*; + +/** + * @author Slimane Amar + */ +// TODO transfer to powsybl-core (com.powsybl.iidm.modification) +// TODO remove public qualifier for all methods +public final class ModificationUtils { + + public static final String DISCONNECTOR = "disconnector_"; + public static final String BREAKER = "breaker_"; + public static final String BUS_BAR_SECTION_ID = "busbarSectionId"; + + public static final String DOES_NOT_EXIST_IN_NETWORK = " does not exist in network"; + public static final String EQUIPMENT_DISCONNECTED = "equipmentDisconnected"; + public static final String NO_VALUE = "No value"; + public static final String LIMITS = "Limits"; + public static final String REACTIVE_LIMITS = "Reactive limits"; + private static final String SETPOINTS = "Setpoints"; + private static final String MIN_REACTIVE_POWER_FIELDNAME = "Minimum reactive power"; + private static final String MAX_REACTIVE_POWER_FIELDNAME = "Maximum reactive power"; + private static final String CONNECTIVITY = "Connectivity"; + public static final String CONNECTION_NAME_FIELD_NAME = "Connection name"; + public static final String CONNECTION_DIRECTION_FIELD_NAME = "Connection direction"; + public static final String CONNECTION_POSITION_FIELD_NAME = "Connection position"; + public static final String NOT_EXIST_IN_NETWORK = " does not exist in network"; + + public enum FeederSide { + INJECTION_SINGLE_SIDE, + BRANCH_SIDE_ONE, + BRANCH_SIDE_TWO + } + + private ModificationUtils() { + } + + public static ModificationUtils getInstance() { + return new ModificationUtils(); + } + + public Double zeroIfNull(Double d) { + return d != null ? d : 0.0; + } + + public static Double nanIfNull(Double d) { + return d == null ? Double.NaN : d; + } + + public VoltageLevel getVoltageLevel(Network network, String voltageLevelId) { + VoltageLevel voltageLevel = network.getVoltageLevel(voltageLevelId); + if (voltageLevel == null) { + throw new NetworkModificationException(VOLTAGE_LEVEL_NOT_FOUND, voltageLevelId); + } + return voltageLevel; + } + + public Line getLine(Network network, String lineId) { + Line line = network.getLine(lineId); + if (line == null) { + throw new NetworkModificationException(LINE_NOT_FOUND, lineId); + } + return line; + } + + public Battery getBattery(Network network, String batteryId) { + Battery battery = network.getBattery(batteryId); + if (battery == null) { + throw new NetworkModificationException(BATTERY_NOT_FOUND, "Battery " + batteryId + NOT_EXIST_IN_NETWORK); + } + return battery; + } + + public Generator getGenerator(Network network, String generatorId) { + Generator generator = network.getGenerator(generatorId); + if (generator == null) { + throw new NetworkModificationException(GENERATOR_NOT_FOUND, "Generator " + generatorId + NOT_EXIST_IN_NETWORK); + } + return generator; + } + + public VscConverterStation getVscConverterStation(Network network, String converterStationId) { + VscConverterStation vscConverterStation = network.getVscConverterStation(converterStationId); + if (vscConverterStation == null) { + throw new NetworkModificationException(VSC_CONVERTER_STATION_NOT_FOUND, "Vsc converter station " + converterStationId + NOT_EXIST_IN_NETWORK); + } + return vscConverterStation; + } + + //get hvdcline + public HvdcLine getHvdcLine(Network network, String hvdcLineId) { + HvdcLine hvdcLine = network.getHvdcLine(hvdcLineId); + if (hvdcLine == null) { + throw new NetworkModificationException(HVDC_LINE_NOT_FOUND, "Hvdc line " + hvdcLineId + NOT_EXIST_IN_NETWORK); + } + return hvdcLine; + } + + public StaticVarCompensator getStaticVarCompensator(Network network, String staticVarCompensatorId) { + StaticVarCompensator staticVarCompensator = network.getStaticVarCompensator(staticVarCompensatorId); + if (staticVarCompensator == null) { + throw new NetworkModificationException(STATIC_VAR_COMPENSATOR_NOT_FOUND, "Static var compensator " + staticVarCompensatorId + DOES_NOT_EXIST_IN_NETWORK); + } + return staticVarCompensator; + } + + public void controlConnectivity(Network network, String voltageLevelId, String busOrBusbarSectionId, Integer connectionPosition) { + VoltageLevel voltageLevel = getVoltageLevel(network, voltageLevelId); + if (voltageLevel.getTopologyKind() == TopologyKind.NODE_BREAKER) { + // bus bar section must exist + controlBus(network, voltageLevel, busOrBusbarSectionId); + // check if position is free + Set takenFeederPositions = TopologyModificationUtils.getFeederPositions(voltageLevel); + var position = getPosition(connectionPosition, busOrBusbarSectionId, network, voltageLevel); + if (takenFeederPositions.contains(position)) { + throw new NetworkModificationException(CONNECTION_POSITION_ERROR, "PositionOrder '" + position + "' already taken"); + } + } else { + // bus breaker must exist + controlBus(network, voltageLevel, busOrBusbarSectionId); + } + } + + public void controlBus(Network network, VoltageLevel voltageLevel, String busOrBusbarSectionId) { + if (voltageLevel.getTopologyKind() == TopologyKind.BUS_BREAKER) { + getBusBreakerBus(voltageLevel, busOrBusbarSectionId); + } else if (network.getBusbarSection(busOrBusbarSectionId) == null) { + throw new NetworkModificationException(BUSBAR_SECTION_NOT_FOUND, busOrBusbarSectionId); + } + } + + public void controlBranchCreation(Network network, String voltageLevelId1, String busOrBusbarSectionId1, Integer connectionPosition1, + String voltageLevelId2, String busOrBusbarSectionId2, Integer connectionPosition2) { + VoltageLevel voltageLevel1 = getVoltageLevel(network, voltageLevelId1); + VoltageLevel voltageLevel2 = getVoltageLevel(network, voltageLevelId2); + if (voltageLevel1.getTopologyKind() == TopologyKind.NODE_BREAKER && + voltageLevel2.getTopologyKind() == TopologyKind.NODE_BREAKER) { + controlConnectivity(network, voltageLevelId1, + busOrBusbarSectionId1, connectionPosition1); + controlConnectivity(network, voltageLevelId2, + busOrBusbarSectionId2, connectionPosition2); + } else { + // bus or mixed mode + controlBus(network, voltageLevel1, busOrBusbarSectionId1); + controlBus(network, voltageLevel2, busOrBusbarSectionId2); + } + } + + public int getPosition(Integer defaultPosition, String busOrBusbarSectionId, Network network, VoltageLevel voltageLevel) { + return defaultPosition != null + ? defaultPosition + : getPosition(busOrBusbarSectionId, network, voltageLevel); + } + + public int getPosition(String busOrBusbarSectionId, Network network, VoltageLevel voltageLevel) { + var position = 0; + var bbs = network.getBusbarSection(busOrBusbarSectionId); + if (bbs == null) { + throw new NetworkModificationException(BUSBAR_SECTION_NOT_FOUND, busOrBusbarSectionId); + } + + var extensionExist = bbs.getExtension(BusbarSectionPosition.class) != null; + if (!extensionExist) { + return position; + } + + if (voltageLevel.getConnectableStream().anyMatch(c -> !(c instanceof BusbarSection))) { + var rightRange = TopologyModificationUtils.getUnusedOrderPositionsAfter(bbs); + if (rightRange.isPresent()) { + position = rightRange.get().getMinimum(); + } else { + var leftRange = TopologyModificationUtils.getUnusedOrderPositionsBefore(bbs); + if (leftRange.isPresent()) { + position = leftRange.get().getMaximum(); + } else { + throw new NetworkModificationException(POSITION_ORDER_ERROR, "no available position"); + } + } + } + + return position; + } + + public Bus getBusBreakerBus(VoltageLevel voltageLevel, String busId) { + VoltageLevel.BusBreakerView busBreakerView = voltageLevel.getBusBreakerView(); + Bus bus = busBreakerView.getBus(busId); + if (bus == null) { + throw new NetworkModificationException(BUS_NOT_FOUND, busId); + } + return bus; + } + + public int createNodeBreakerCellSwitches(VoltageLevel voltageLevel, String busBarSectionId, String equipmentId, + String equipmentName, String sideSuffix) { + VoltageLevel.NodeBreakerView nodeBreakerView = voltageLevel.getNodeBreakerView(); + BusbarSection busbarSection = nodeBreakerView.getBusbarSection(busBarSectionId); + if (busbarSection == null) { + throw new NetworkModificationException(BUSBAR_SECTION_NOT_FOUND, busBarSectionId); + } + + // creating the disconnector + int newNode = nodeBreakerView.getMaximumNodeIndex(); + String disconnectorId = DISCONNECTOR + equipmentId + sideSuffix; + String disconnectorName = equipmentName != null ? DISCONNECTOR + equipmentName + sideSuffix : null; + nodeBreakerView.newSwitch() + .setId(disconnectorId) + .setName(disconnectorName) + .setKind(SwitchKind.DISCONNECTOR) + .setRetained(false) + .setOpen(false) + .setFictitious(false) + .setNode1(busbarSection.getTerminal().getNodeBreakerView().getNode()) + .setNode2(newNode + 1) + .add(); + + // creating the breaker + String breakerId = BREAKER + equipmentId + sideSuffix; + String breakerName = equipmentName != null ? BREAKER + equipmentName + sideSuffix : null; + nodeBreakerView.newSwitch() + .setId(breakerId) + .setName(breakerName) + .setKind(SwitchKind.BREAKER) + .setRetained(false) + .setOpen(false) + .setFictitious(false) + .setNode1(newNode + 1) + .setNode2(newNode + 2) + .add(); + + return newNode + 2; + } + + public void controlNewOrExistingVoltageLevel(VoltageLevelCreationInfos mayNewVL, + String existingVoltageLevelId, String bbsOrBusId, Network network) { + if (mayNewVL != null) { + controlVoltageLevelCreation(mayNewVL, network); + } else { + // use existing VL + VoltageLevel vl = network.getVoltageLevel(existingVoltageLevelId); + if (vl == null) { + throw new NetworkModificationException(VOLTAGE_LEVEL_NOT_FOUND, existingVoltageLevelId); + } + // check existing busbar/bus + controlBus(network, vl, bbsOrBusId); + } + } + + public void controlVoltageLevelCreation(VoltageLevelCreationInfos voltageLevelCreationInfos, Network network) { + if (network.getVoltageLevel(voltageLevelCreationInfos.getEquipmentId()) != null) { + throw new NetworkModificationException(VOLTAGE_LEVEL_ALREADY_EXISTS, voltageLevelCreationInfos.getEquipmentId()); + } + if (voltageLevelCreationInfos.getCouplingDevices().stream() + .anyMatch(cd -> cd.getBusbarSectionId1().equals(cd.getBusbarSectionId2()))) { + throw new NetworkModificationException(CREATE_VOLTAGE_LEVEL_ERROR, + "Coupling between same bus bar section is not allowed"); + } + if (Objects.nonNull(voltageLevelCreationInfos.getIpMin()) && voltageLevelCreationInfos.getIpMin() < 0) { + throw new NetworkModificationException(CREATE_VOLTAGE_LEVEL_ERROR, "IpMin must be positive"); + } + if (Objects.nonNull(voltageLevelCreationInfos.getIpMax()) && voltageLevelCreationInfos.getIpMax() < 0) { + throw new NetworkModificationException(CREATE_VOLTAGE_LEVEL_ERROR, "IpMax must be positive"); + } + if (Objects.nonNull(voltageLevelCreationInfos.getIpMin()) && Objects.isNull(voltageLevelCreationInfos.getIpMax())) { + throw new NetworkModificationException(CREATE_VOLTAGE_LEVEL_ERROR, "IpMax is required"); + } + if (Objects.nonNull(voltageLevelCreationInfos.getIpMin()) && Objects.nonNull(voltageLevelCreationInfos.getIpMax()) + && voltageLevelCreationInfos.getIpMin() > voltageLevelCreationInfos.getIpMax()) { + throw new NetworkModificationException(CREATE_VOLTAGE_LEVEL_ERROR, "IpMin cannot be greater than IpMax"); + } + } + + private boolean checkBbs(Network network, String busbarSectionId1, String busbarSectionId2, ReportNode subReportNode) { + Identifiable busOrBbs1 = network.getIdentifiable(busbarSectionId1); + Identifiable busOrBbs2 = network.getIdentifiable(busbarSectionId2); + if (busOrBbs1 == null) { + subReportNode.newReportNode() + .withMessageTemplate("notFoundBurOrBusbarSection", "Bus or busbar section ID ${busbarSectionId} not found. Coupler was not created.") + .withUntypedValue(BUS_BAR_SECTION_ID, busbarSectionId1) + .withSeverity(TypedValue.ERROR_SEVERITY) + .add(); + return false; + } + if (busOrBbs2 == null) { + subReportNode.newReportNode() + .withMessageTemplate("notFoundBurOrBusbarSection", "Bus or busbar section ID ${busbarSectionId} not found. Coupler was not created.") + .withUntypedValue(BUS_BAR_SECTION_ID, busbarSectionId2) + .withSeverity(TypedValue.ERROR_SEVERITY) + .add(); + return false; + } + return true; + } + + public void createVoltageLevel(VoltageLevelCreationInfos voltageLevelCreationInfos, + ReportNode subReportNode, Network network) { + String substationId = voltageLevelCreationInfos.getSubstationId(); + Substation substation = network.getSubstation(substationId); + if (substation == null) { + throw new NetworkModificationException(SUBSTATION_NOT_FOUND, substationId); + } + + VoltageLevel voltageLevel = substation.newVoltageLevel() + .setId(voltageLevelCreationInfos.getEquipmentId()) + .setName(voltageLevelCreationInfos.getEquipmentName()) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(voltageLevelCreationInfos.getNominalV()) + .add(); + + if (voltageLevelCreationInfos.getLowVoltageLimit() != null) { + voltageLevel.setLowVoltageLimit(voltageLevelCreationInfos.getLowVoltageLimit()); + } + if (voltageLevelCreationInfos.getHighVoltageLimit() != null) { + voltageLevel.setHighVoltageLimit(voltageLevelCreationInfos.getHighVoltageLimit()); + } + + if (voltageLevelCreationInfos.getIpMax() != null && voltageLevelCreationInfos.getIpMin() != null) { + voltageLevel.newExtension(IdentifiableShortCircuitAdder.class) + .withIpMin(voltageLevelCreationInfos.getIpMin()) + .withIpMax(voltageLevelCreationInfos.getIpMax()) + .add(); + } else if (voltageLevelCreationInfos.getIpMax() != null && voltageLevelCreationInfos.getIpMin() == null) { + voltageLevel.newExtension(IdentifiableShortCircuitAdder.class) + .withIpMax(voltageLevelCreationInfos.getIpMax()) + .add(); + } else if (voltageLevelCreationInfos.getIpMax() == null && voltageLevelCreationInfos.getIpMin() != null) { + voltageLevel.newExtension(IdentifiableShortCircuitAdder.class) + .withIpMin(voltageLevelCreationInfos.getIpMin()) + .add(); + } + + CreateVoltageLevelTopologyBuilder voltageLevelTopologyBuilder = new CreateVoltageLevelTopologyBuilder(); + voltageLevelTopologyBuilder.withVoltageLevelId(voltageLevelCreationInfos.getEquipmentId()) + .withAlignedBusesOrBusbarCount(voltageLevelCreationInfos.getBusbarCount()) + .withSectionCount(voltageLevelCreationInfos.getSectionCount()) + .withSwitchKinds(voltageLevelCreationInfos.getSwitchKinds()) + .build().apply(network); + + voltageLevelCreationInfos.getCouplingDevices().forEach(couplingDevice -> { + if (!checkBbs(network, couplingDevice.getBusbarSectionId1(), couplingDevice.getBusbarSectionId2(), subReportNode)) { + return; + } + CreateCouplingDeviceBuilder couplingDeviceBuilder = new CreateCouplingDeviceBuilder(); + couplingDeviceBuilder.withBusOrBusbarSectionId1(couplingDevice.getBusbarSectionId1()) + .withBusOrBusbarSectionId2(couplingDevice.getBusbarSectionId2()) + .withSwitchPrefixId(voltageLevelCreationInfos.getEquipmentId() + "_COUPL") + .build().apply(network, subReportNode); + }); + + subReportNode.newReportNode() + .withMessageTemplate("voltageLevelCreated", "New voltage level with id=${id} created") + .withUntypedValue("id", voltageLevelCreationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + + public LineAdder createLineAdder(Network network, VoltageLevel voltageLevel1, VoltageLevel voltageLevel2, LineCreationInfos lineCreationInfos, boolean withSwitch1, boolean withSwitch2) { + + // common settings + LineAdder lineAdder = network.newLine() + .setId(lineCreationInfos.getEquipmentId()) + .setName(lineCreationInfos.getEquipmentName()) + .setVoltageLevel1(lineCreationInfos.getVoltageLevelId1()) + .setVoltageLevel2(lineCreationInfos.getVoltageLevelId2()) + .setR(lineCreationInfos.getR()) + .setX(lineCreationInfos.getX()) + .setG1(lineCreationInfos.getG1() != null ? lineCreationInfos.getG1() : 0.0) + .setB1(lineCreationInfos.getB1() != null ? lineCreationInfos.getB1() : 0.0) + .setG2(lineCreationInfos.getG2() != null ? lineCreationInfos.getG2() : 0.0) + .setB2(lineCreationInfos.getB2() != null ? lineCreationInfos.getB2() : 0.0); + + // lineAdder completion by topology + setBranchAdderNodeOrBus(lineAdder, voltageLevel1, lineCreationInfos, TwoSides.ONE, withSwitch1); + setBranchAdderNodeOrBus(lineAdder, voltageLevel2, lineCreationInfos, TwoSides.TWO, withSwitch2); + + return lineAdder; + } + + public void setBranchAdderNodeOrBus(BranchAdder branchAdder, VoltageLevel voltageLevel, BranchCreationInfos branchCreationInfos, + TwoSides side, boolean withSwitch) { + String busOrBusbarSectionId = (side == TwoSides.ONE) ? branchCreationInfos.getBusOrBusbarSectionId1() : branchCreationInfos.getBusOrBusbarSectionId2(); + if (voltageLevel.getTopologyKind() == TopologyKind.BUS_BREAKER) { + setBranchAdderBusBreaker(branchAdder, voltageLevel, side, busOrBusbarSectionId); + } else { + if (withSwitch) { // NODE_BREAKER + setBranchAdderNodeBreaker(branchAdder, voltageLevel, branchCreationInfos, side, busOrBusbarSectionId); + } + } + } + + private void setBranchAdderBusBreaker(BranchAdder branchAdder, VoltageLevel voltageLevel, TwoSides side, String busId) { + Bus bus = getBusBreakerBus(voltageLevel, busId); + + // complete the lineAdder + if (side == TwoSides.ONE) { + branchAdder.setBus1(bus.getId()).setConnectableBus1(bus.getId()); + } else { + branchAdder.setBus2(bus.getId()).setConnectableBus2(bus.getId()); + } + } + + private void setBranchAdderNodeBreaker(BranchAdder branchAdder, VoltageLevel voltageLevel, + BranchCreationInfos branchCreationInfos, TwoSides side, + String currentBusBarSectionId) { + // create cell switches + String sideSuffix = side != null ? "_" + side.name() : ""; + int nodeNum = createNodeBreakerCellSwitches(voltageLevel, + currentBusBarSectionId, + branchCreationInfos.getEquipmentId(), + branchCreationInfos.getEquipmentName(), + sideSuffix); + + // complete the lineAdder + if (side == TwoSides.ONE) { + branchAdder.setNode1(nodeNum); + } else { + branchAdder.setNode2(nodeNum); + } + } + + public static void createReport(ReportNode reportNode, String reporterKey, String defaultMessage, Map values, TypedValue errorSeverity) { + ReportNodeAdder adder = reportNode.newReportNode() + .withMessageTemplate(reporterKey, defaultMessage) + .withSeverity(errorSeverity); + + for (Map.Entry valueEntry : values.entrySet()) { + adder.withUntypedValue(valueEntry.getKey(), valueEntry.getValue().toString()); + } + adder.add(); + } + + public static Predicate distinctByKey( + Function keyExtractor) { + + Map seen = new ConcurrentHashMap<>(); + return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; + } + + public ReportNode applyElementaryModificationsAndReturnReport(Consumer setter, Supplier getter, + AttributeModification modification, String fieldName) { + if (modification != null) { + T oldValue = getter.get(); + T newValue = modification.applyModification(oldValue); + setter.accept(newValue); + + return buildModificationReport(oldValue, newValue, fieldName); + } + return null; + } + + public ReportNode applyElementaryModificationsAndReturnReport(Consumer setter, Supplier getter, + AttributeModification modification, String fieldName, int indentationLevel) { + if (modification != null) { + T oldValue = getter.get(); + T newValue = modification.applyModification(oldValue); + setter.accept(newValue); + + return buildModificationReport(oldValue, newValue, fieldName, indentationLevel); + } + return null; + } + + public ReportNode createEnabledDisabledReport(String key, boolean enabled) { + return ReportNode.newRootReportNode() + .withMessageTemplate(key, " ${status}") + .withUntypedValue("status", enabled ? "Enabled" : "Disabled") + .withSeverity(TypedValue.INFO_SEVERITY) + .build(); + } + + public ReportNode reportModifications(ReportNode reportNode, List reports, String subReportNodeKey, String subReportNodeMessage) { + List validReports = reports.stream().filter(Objects::nonNull).toList(); + ReportNode subReportNode = null; + if (!validReports.isEmpty() && reportNode != null) { + // new child report node + subReportNode = reportNode.newReportNode().withMessageTemplate(subReportNodeKey, subReportNodeMessage).add(); + for (ReportNode report : validReports) { + ReportNodeAdder reportNodeAdder = subReportNode.newReportNode().withMessageTemplate(report.getMessageKey(), report.getMessageTemplate()).withSeverity(TypedValue.INFO_SEVERITY); + for (Map.Entry valueEntry : report.getValues().entrySet()) { + reportNodeAdder.withUntypedValue(valueEntry.getKey(), valueEntry.getValue().toString()); + } + report.getValue(ReportConstants.SEVERITY_KEY).ifPresent(reportNodeAdder::withSeverity); + reportNodeAdder.add(); + } + } + return subReportNode; + } + + public void applyElementaryModifications(Consumer setter, Supplier getter, + AttributeModification modification, + ReportNode subReportNode, String fieldName) { + if (modification != null) { + T oldValue = getter.get(); + T newValue = modification.applyModification(oldValue); + setter.accept(newValue); + + if (subReportNode != null) { + insertReportNode(subReportNode, buildModificationReport(oldValue, newValue, fieldName)); + } + } + } + + public ReportNode applyAndBuildModificationReport(Consumer setter, Supplier getter, AttributeModification modification, String fieldName) { + T oldValue = getter.get(); + T newValue = modification.applyModification(oldValue); + setter.accept(newValue); + return buildModificationReport(oldValue, newValue, fieldName, 1, TypedValue.INFO_SEVERITY); + } + + public ReportNode buildModificationReport(T oldValue, T newValue, String fieldName) { + return buildModificationReport(oldValue, newValue, fieldName, 1, TypedValue.INFO_SEVERITY); + } + + public ReportNode buildModificationReport(T oldValue, T newValue, String fieldName, int indentationLevel) { + return buildModificationReport(oldValue, newValue, fieldName, indentationLevel, TypedValue.INFO_SEVERITY); + } + + public static ReportNode buildModificationReport(T oldValue, T newValue, String fieldName, int indentationLevel, TypedValue severity) { + final String oldValueString = (oldValue == null || oldValue instanceof Double oldDouble && Double.isNaN(oldDouble)) + ? NO_VALUE : oldValue.toString(); + final String newValueString = (newValue == null || newValue instanceof Double newDouble && Double.isNaN(newDouble)) + ? NO_VALUE : newValue.toString(); + final String indentation = "\t".repeat(indentationLevel); + return ReportNode.newRootReportNode() + .withMessageTemplate("modification-indent" + indentationLevel, indentation + "${fieldName} : ${oldValue} → ${newValue}") + .withUntypedValue("fieldName", fieldName) + .withUntypedValue("oldValue", oldValueString) + .withUntypedValue("newValue", newValueString) + .withSeverity(severity) + .build(); + } + + public Terminal getTerminalFromIdentifiable(Network network, String equipmentId, String type, String voltageLevelId) { + if (network != null && equipmentId != null && type != null && voltageLevelId != null) { + Identifiable identifiable = getEquipmentByIdentifiableType(network, IdentifiableType.valueOf(type), equipmentId); + + if (identifiable == null) { + throw new NetworkModificationException(EQUIPMENT_NOT_FOUND, "Equipment with id=" + equipmentId + " not found with type " + type); + } + + if (identifiable instanceof Injection) { + return ((Injection) identifiable).getTerminal(); + } else if (identifiable instanceof Branch) { + return ((Branch) identifiable).getTerminal(voltageLevelId); + } + } + + return null; + } + + public List getTerminalsFromIdentifiable(Identifiable identifiable) { + if (identifiable instanceof Branch branch) { + return Stream.of( + branch.getTerminal1(), + branch.getTerminal2() + ).toList(); + } else if (identifiable instanceof ThreeWindingsTransformer w3t) { + return Stream.of( + w3t.getLeg1().getTerminal(), + w3t.getLeg2().getTerminal(), + w3t.getLeg3().getTerminal() + ).toList(); + } else if (identifiable instanceof HvdcLine hvdcLine) { + return Stream.of( + hvdcLine.getConverterStation1().getTerminal(), + hvdcLine.getConverterStation2().getTerminal() + ).toList(); + } + throw NetworkModificationException.createEquipmentTypeNotSupported(identifiable.getClass().getSimpleName()); + } + + public static boolean isInjectionConnected(Injection injection) { + return injection != null && injection.getTerminal().isConnected(); + } + + public void disconnectCreatedInjection(InjectionCreationInfos modificationInfos, Injection injection, ReportNode subReportNode) { + // A newly created injection is connected by default, unless we choose not to do + if (!modificationInfos.isTerminalConnected()) { + injection.getTerminal().disconnect(); + subReportNode.newReportNode() + .withMessageTemplate(EQUIPMENT_DISCONNECTED, "Equipment with id=${id} disconnected") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + } + + public ReportNode modifyInjectionConnectivityAttributes(ConnectablePosition connectablePosition, + ConnectablePositionAdder connectablePositionAdder, + Injection injection, + InjectionModificationInfos modificationInfos, + ReportNode connectivityReports) { + List reports = new ArrayList<>(); + processConnectivityPosition(connectablePosition, connectablePositionAdder, modificationInfos, injection.getNetwork(), reports); + modifyConnection(modificationInfos.getTerminalConnected(), injection, injection.getTerminal(), reports); + + return reportModifications(connectivityReports, reports, "ConnectivityModified", CONNECTIVITY); + } + + public ReportNode modifyBranchConnectivityAttributes(ConnectablePosition connectablePosition, + ConnectablePositionAdder connectablePositionAdder, + Branch branch, + BranchModificationInfos modificationInfos, + ReportNode connectivityReports) { + List reports = new ArrayList<>(); + processConnectivityPosition(connectablePosition, connectablePositionAdder, modificationInfos, branch.getNetwork(), reports); + modifyConnection(modificationInfos.getTerminal1Connected(), branch, branch.getTerminal1(), reports); + modifyConnection(modificationInfos.getTerminal2Connected(), branch, branch.getTerminal2(), reports); + + return reportModifications(connectivityReports, reports, "ConnectivityModified", CONNECTIVITY); + } + + private void processConnectivityPosition(ConnectablePosition connectablePosition, + ConnectablePositionAdder connectablePositionAdder, + BasicEquipmentModificationInfos modificationInfos, + Network network, + List reports) { + if (connectablePosition != null) { + modifyExistingConnectivityPosition(connectablePosition, modificationInfos, reports); + } else { + createNewConnectivityPosition(connectablePositionAdder, modificationInfos, network, reports); + } + } + + private void modifyExistingConnectivityPosition(ConnectablePosition connectablePosition, + BasicEquipmentModificationInfos modificationInfos, + List reports) { + if (modificationInfos instanceof BranchModificationInfos) { + modifyConnectablePosition(connectablePosition.getFeeder1(), modificationInfos, reports, FeederSide.BRANCH_SIDE_ONE); + modifyConnectablePosition(connectablePosition.getFeeder2(), modificationInfos, reports, FeederSide.BRANCH_SIDE_TWO); + } else if (modificationInfos instanceof InjectionModificationInfos) { + modifyConnectablePosition(connectablePosition.getFeeder(), modificationInfos, reports, FeederSide.INJECTION_SINGLE_SIDE); + } + } + + private void createNewConnectivityPosition(ConnectablePositionAdder adder, + BasicEquipmentModificationInfos modificationInfos, + Network network, + List reports) { + if (modificationInfos instanceof BranchModificationInfos) { + addConnectablePosition(adder, modificationInfos, network, reports, FeederSide.BRANCH_SIDE_ONE); + addConnectablePosition(adder, modificationInfos, network, reports, FeederSide.BRANCH_SIDE_TWO); + } else if (modificationInfos instanceof InjectionModificationInfos) { + addConnectablePosition(adder, modificationInfos, network, reports, FeederSide.INJECTION_SINGLE_SIDE); + } + } + + private void modifyConnectablePosition(ConnectablePosition.Feeder feeder, + BasicEquipmentModificationInfos modificationInfos, + List reports, + FeederSide feederSide) { + applyModifications(feeder, modificationInfos, reports, feederSide); + } + + private void applyModifications(ConnectablePosition.Feeder feeder, + BasicEquipmentModificationInfos modificationInfos, + List reports, + FeederSide feederSide) { + ReportNode connectionNameReport = applyElementaryModificationsAndReturnReport(feeder::setName, + feeder.getName()::get, + getConnectionName(modificationInfos, feederSide), + getConnectionNameField(feederSide)); + if (connectionNameReport != null) { + reports.add(connectionNameReport); + } + ReportNode connectionDirectionReport = applyElementaryModificationsAndReturnReport(feeder::setDirection, + feeder::getDirection, + getConnectionDirection(modificationInfos, feederSide), + getConnectionDirectionField(feederSide)); + if (connectionDirectionReport != null) { + reports.add(connectionDirectionReport); + } + ReportNode connectionPositionReport = applyElementaryModificationsAndReturnReport(feeder::setOrder, + feeder.getOrder()::get, + getConnectionPosition(modificationInfos, feederSide), + getConnectionPositionField(feederSide)); + if (connectionPositionReport != null) { + reports.add(connectionPositionReport); + } + } + + private void addConnectablePosition(ConnectablePositionAdder adder, + BasicEquipmentModificationInfos modificationInfos, + Network network, + List reports, + FeederSide feederSide) { + AttributeModification connectionName = getConnectionName(modificationInfos, feederSide); + AttributeModification connectionDirection = getConnectionDirection(modificationInfos, feederSide); + AttributeModification connectionPosition = getConnectionPosition(modificationInfos, feederSide); + if (Objects.isNull(connectionName) && Objects.isNull(connectionDirection) && Objects.isNull(connectionPosition)) { + return; + } + AttributeModification equipmentId = getEquipmentId(modificationInfos); + AttributeModification voltageLevelId = getVoltageLevelId(modificationInfos, feederSide); + AttributeModification busOrBusbarSectionId = getBusOrBusbarSectionId(modificationInfos, feederSide); + int position = getPosition(connectionPosition, busOrBusbarSectionId, voltageLevelId, equipmentId, feederSide, network); + ConnectablePositionAdder.FeederAdder feeder; + switch (feederSide) { + case INJECTION_SINGLE_SIDE -> feeder = adder.newFeeder(); + case BRANCH_SIDE_ONE -> feeder = adder.newFeeder1(); + case BRANCH_SIDE_TWO -> feeder = adder.newFeeder2(); + default -> { + return; + } + } + ReportNode connectionNameReport = applyConnectablePositionAttribute( + feeder::withName, connectionName, equipmentId, reports, + getConnectionNameField(feederSide), connectionDirection, connectionPosition + ); + + ReportNode connectionDirectionReport = applyConnectablePositionAttribute( + feeder::withDirection, connectionDirection, + new AttributeModification<>(ConnectablePosition.Direction.UNDEFINED, OperationType.SET), + reports, getConnectionDirectionField(feederSide), + connectionName, connectionPosition + ); + + ReportNode connectionPositionReport = applyConnectablePositionAttribute( + feeder::withOrder, connectionPosition, + new AttributeModification<>(position, OperationType.SET), + reports, getConnectionPositionField(feederSide), + connectionName, connectionDirection + ); + + if (connectionNameReport != null || connectionDirectionReport != null || connectionPositionReport != null) { + feeder.add(); + adder.add(); + } + } + + private ReportNode applyConnectablePositionAttribute(Consumer setter, + AttributeModification newValue, + AttributeModification defaultValue, + List reports, + String fieldName, + AttributeModification... dependentAttributes) { + + AttributeModification finalModification = (newValue == null && isAnyAttributesNonNull(dependentAttributes)) + ? defaultValue + : newValue; + + ReportNode report = applyElementaryModificationsAndReturnReport(setter, () -> null, finalModification, fieldName); + if (report != null) { + reports.add(report); + } + return report; + } + + private boolean isAnyAttributesNonNull(AttributeModification... attributes) { + return Arrays.stream(attributes).anyMatch(Objects::nonNull); + } + + private T getConnectionDetail(BasicEquipmentModificationInfos modificationInfos, FeederSide feederSide, + Function branchFunc1, + Function branchFunc2, + Function injectionFunc) { + if (modificationInfos instanceof BranchModificationInfos branchInfo) { + if (Objects.requireNonNull(feederSide) == FeederSide.BRANCH_SIDE_ONE) { + return branchFunc1.apply(branchInfo); + } else if (feederSide == FeederSide.BRANCH_SIDE_TWO) { + return branchFunc2.apply(branchInfo); + } + } else if (modificationInfos instanceof InjectionModificationInfos injectionInfo) { + return injectionFunc.apply(injectionInfo); + } + return null; + } + + private String getConnectionFieldName(FeederSide feederSide, String baseFieldName) { + return switch (feederSide) { + case INJECTION_SINGLE_SIDE -> baseFieldName; + case BRANCH_SIDE_ONE -> baseFieldName + " 1"; + case BRANCH_SIDE_TWO -> baseFieldName + " 2"; + }; + } + + private AttributeModification getVoltageLevelId(BasicEquipmentModificationInfos modificationInfos, FeederSide feederSide) { + return getConnectionDetail(modificationInfos, feederSide, + BranchModificationInfos::getVoltageLevelId1, BranchModificationInfos::getVoltageLevelId2, + InjectionModificationInfos::getVoltageLevelId); + } + + private AttributeModification getBusOrBusbarSectionId(BasicEquipmentModificationInfos modificationInfos, FeederSide feederSide) { + return getConnectionDetail(modificationInfos, feederSide, + BranchModificationInfos::getBusOrBusbarSectionId1, BranchModificationInfos::getBusOrBusbarSectionId2, + InjectionModificationInfos::getBusOrBusbarSectionId); + } + + private AttributeModification getEquipmentId(BasicEquipmentModificationInfos modificationInfos) { + return AttributeModification.toAttributeModification(modificationInfos.getEquipmentId(), OperationType.SET); + } + + private AttributeModification getConnectionName(BasicEquipmentModificationInfos modificationInfos, FeederSide feederSide) { + return getConnectionDetail(modificationInfos, feederSide, + BranchModificationInfos::getConnectionName1, BranchModificationInfos::getConnectionName2, + InjectionModificationInfos::getConnectionName); + } + + private String getConnectionNameField(FeederSide feederSide) { + return getConnectionFieldName(feederSide, CONNECTION_NAME_FIELD_NAME); + } + + private String getConnectionDirectionField(FeederSide feederSide) { + return getConnectionFieldName(feederSide, CONNECTION_DIRECTION_FIELD_NAME); + } + + private String getConnectionPositionField(FeederSide feederSide) { + return getConnectionFieldName(feederSide, CONNECTION_POSITION_FIELD_NAME); + } + + private AttributeModification getConnectionDirection(BasicEquipmentModificationInfos modificationInfos, FeederSide feederSide) { + return getConnectionDetail(modificationInfos, feederSide, + BranchModificationInfos::getConnectionDirection1, BranchModificationInfos::getConnectionDirection2, + InjectionModificationInfos::getConnectionDirection); + } + + private AttributeModification getConnectionPosition(BasicEquipmentModificationInfos modificationInfos, FeederSide feederSide) { + return getConnectionDetail(modificationInfos, feederSide, + BranchModificationInfos::getConnectionPosition1, BranchModificationInfos::getConnectionPosition2, + InjectionModificationInfos::getConnectionPosition); + } + + private String getBusOrBusbarSection(Terminal terminal) { + String busOrBusbarSectionId; + if (terminal.getVoltageLevel().getTopologyKind().equals(TopologyKind.BUS_BREAKER)) { + if (terminal.isConnected()) { + busOrBusbarSectionId = terminal.getBusBreakerView().getBus().getId(); + } else { + busOrBusbarSectionId = terminal.getBusBreakerView().getConnectableBus().getId(); + } + } else { + busOrBusbarSectionId = getBusbarSectionId(terminal); + } + return busOrBusbarSectionId; + } + + private String getBusbarSectionId(Terminal terminal) { + BusbarSectionFinderTraverser connectedBusbarSectionFinder = new BusbarSectionFinderTraverser(terminal.isConnected()); + terminal.traverse(connectedBusbarSectionFinder, TraversalType.BREADTH_FIRST); + return connectedBusbarSectionFinder.getFirstTraversedBbsId(); + } + + private int getPosition(AttributeModification connectionPosition, + AttributeModification busOrBusbarSectionId, + AttributeModification voltageLevelId, + AttributeModification equipmentId, + FeederSide feederSide, + Network network) { + String equipmentValue = equipmentId.getValue(); + Terminal selectedTerminal = null; + switch (feederSide) { + case INJECTION_SINGLE_SIDE -> selectedTerminal = network.getIdentifiable(equipmentValue) instanceof Injection injection ? injection.getTerminal() : null; + case BRANCH_SIDE_ONE -> selectedTerminal = network.getIdentifiable(equipmentValue) instanceof Branch branch ? branch.getTerminal1() : null; + case BRANCH_SIDE_TWO -> selectedTerminal = network.getIdentifiable(equipmentValue) instanceof Branch branch ? branch.getTerminal2() : null; + } + String voltageLevel = (voltageLevelId != null && voltageLevelId.getValue() != null) + ? voltageLevelId.getValue() + : Optional.ofNullable(selectedTerminal).map(terminal -> terminal.getVoltageLevel().getId()).orElse(null); + String busOrBusbarSection = (busOrBusbarSectionId != null && busOrBusbarSectionId.getValue() != null) + ? busOrBusbarSectionId.getValue() + : Optional.ofNullable(selectedTerminal).map(this::getBusOrBusbarSection).orElse(null); + Integer connectionPositionValue = (connectionPosition != null) ? connectionPosition.getValue() : null; + return getPosition(connectionPositionValue, busOrBusbarSection, network, getVoltageLevel(network, voltageLevel)); + } + + private void modifyConnection(AttributeModification terminalConnected, Identifiable equipment, Terminal terminal, List reports) { + if (terminalConnected == null || equipment == null) { + return; + } + + boolean isConnected = terminal.isConnected(); + if (isConnected && Boolean.FALSE.equals(terminalConnected.getValue())) { + terminal.disconnect(); + validateConnectionChange(!terminal.isConnected(), equipment, "disconnect", reports); + } else if (!isConnected && Boolean.TRUE.equals(terminalConnected.getValue())) { + terminal.connect(); + validateConnectionChange(terminal.isConnected(), equipment, "connect", reports); + } + } + + private void validateConnectionChange(boolean success, Identifiable equipment, String action, List reports) { + if (!success) { + throw new NetworkModificationException(equipment instanceof Branch ? BRANCH_MODIFICATION_ERROR : INJECTION_MODIFICATION_ERROR, + String.format("Could not %s equipment '%s'", action, equipment.getId())); + } + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("equipment" + capitalize(action), String.format("Equipment with id=${id} %sed", action)) + .withUntypedValue("id", equipment.getId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + } + + private String capitalize(String input) { + return input.substring(0, 1).toUpperCase() + input.substring(1); + } + + public void disconnectBranch(BranchCreationInfos modificationInfos, Branch branch, ReportNode subReportNode) { + // A newly created branch is connected by default on both sides, unless we choose not to do + if (!modificationInfos.isConnected1()) { + branch.getTerminal1().disconnect(); + subReportNode.newReportNode() + .withMessageTemplate("terminal1Disconnected", "Equipment with id=${id} disconnected on side 1") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + if (!modificationInfos.isConnected2()) { + branch.getTerminal2().disconnect(); + subReportNode.newReportNode() + .withMessageTemplate("terminal2Disconnected", "Equipment with id=${id} disconnected on side 2") + .withUntypedValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .add(); + } + } + + public Identifiable getEquipmentByIdentifiableType(Network network, IdentifiableType type, String equipmentId) { + if (type == null || equipmentId == null) { + return null; + } + + return switch (type) { + case HVDC_LINE -> network.getHvdcLine(equipmentId); + case LINE -> network.getLine(equipmentId); + case TWO_WINDINGS_TRANSFORMER -> network.getTwoWindingsTransformer(equipmentId); + case THREE_WINDINGS_TRANSFORMER -> network.getThreeWindingsTransformer(equipmentId); + case GENERATOR -> network.getGenerator(equipmentId); + case LOAD -> network.getLoad(equipmentId); + case BATTERY -> network.getBattery(equipmentId); + case SHUNT_COMPENSATOR -> network.getShuntCompensator(equipmentId); + case STATIC_VAR_COMPENSATOR -> network.getStaticVarCompensator(equipmentId); + case DANGLING_LINE -> network.getDanglingLine(equipmentId); + case HVDC_CONVERTER_STATION -> network.getHvdcConverterStation(equipmentId); + case SUBSTATION -> network.getSubstation(equipmentId); + case VOLTAGE_LEVEL -> network.getVoltageLevel(equipmentId); + case BUSBAR_SECTION -> network.getBusbarSection(equipmentId); + default -> null; + }; + } + + public void setCurrentLimits(CurrentLimitsInfos currentLimitsInfos, CurrentLimitsAdder limitsAdder) { + if (currentLimitsInfos != null) { + boolean hasPermanent = currentLimitsInfos.getPermanentLimit() != null; + boolean hasTemporary = currentLimitsInfos.getTemporaryLimits() != null && !currentLimitsInfos.getTemporaryLimits().isEmpty(); + if (hasPermanent) { + limitsAdder.setPermanentLimit(currentLimitsInfos.getPermanentLimit()); + } + if (hasTemporary) { + for (CurrentTemporaryLimitCreationInfos limit : currentLimitsInfos.getTemporaryLimits()) { + limitsAdder + .beginTemporaryLimit() + .setName(limit.getName()) + .setValue(limit.getValue() == null ? Double.MAX_VALUE : limit.getValue()) + .setAcceptableDuration(limit.getAcceptableDuration() == null ? Integer.MAX_VALUE : limit.getAcceptableDuration()) + .endTemporaryLimit(); + } + } + if (hasPermanent || hasTemporary) { + limitsAdder.add(); + } + } + } + + public ReportNode buildCreationReport(T value, String fieldName) { + String newValueString = value == null ? NO_VALUE : value.toString(); + return ReportNode.newRootReportNode() + .withMessageTemplate("Creation" + fieldName, " ${fieldName} : ${value}") + .withUntypedValue("fieldName", fieldName) + .withUntypedValue("value", newValueString) + .withSeverity(TypedValue.INFO_SEVERITY) + .build(); + } + + public void reportElementaryCreation(ReportNode subReportNode, T value, String fieldName) { + insertReportNode(subReportNode, buildCreationReport(value, fieldName)); + } + + public String formatRegulationModeReport(PhaseTapChanger.RegulationMode regulationMode) { + return switch (regulationMode) { + case FIXED_TAP -> " Fixed tap"; + case CURRENT_LIMITER -> " Current limiter"; + case ACTIVE_POWER_CONTROL -> " Active power control"; + }; + } + + public void modifyReactiveCapabilityCurvePoints(Collection points, + List modificationPoints, + ReactiveCapabilityCurveAdder adder, + ReportNode subReportNode, ReportNode subReportNodeLimits) { + List reports = new ArrayList<>(); + List equipementIdPoints = new ArrayList<>(points); + IntStream.range(0, modificationPoints.size()) + .forEach(i -> { + String fieldSuffix; + ReactiveCapabilityCurve.Point oldPoint = i < equipementIdPoints.size() - 1 ? equipementIdPoints.get(i) : null; + ReactiveCapabilityCurveModificationInfos newPoint = modificationPoints.get(i); + if (i == 0) { + fieldSuffix = "min"; + } else if (i == (modificationPoints.size() - 1)) { + fieldSuffix = "max"; + if (!CollectionUtils.isEmpty(equipementIdPoints)) { + oldPoint = equipementIdPoints.get(equipementIdPoints.size() - 1); + } + } else { + fieldSuffix = Integer.toString(i); + } + createReactiveCapabilityCurvePoint(adder, newPoint, oldPoint, reports, fieldSuffix); + }); + adder.add(); + ReportNode subReportNodeReactiveLimits = null; + ReportNode subReporterLimits2 = subReportNodeLimits; + if (!reports.isEmpty()) { + if (subReportNodeLimits == null) { + subReporterLimits2 = subReportNode.newReportNode().withMessageTemplate(LIMITS, LIMITS).add(); + } + if (subReporterLimits2 != null) { + subReportNodeReactiveLimits = subReporterLimits2.newReportNode().withMessageTemplate(REACTIVE_LIMITS, REACTIVE_LIMITS).add(); + } + } + reportModifications(subReportNodeReactiveLimits, reports, "curveReactiveLimitsModified", "By diagram"); + } + + public void createReactiveCapabilityCurvePoint(ReactiveCapabilityCurveAdder adder, + ReactiveCapabilityCurveModificationInfos newPoint, + ReactiveCapabilityCurve.Point oldPoint, + List reports, + String fieldSuffix) { + Double oldMaxQ = Double.NaN; + Double oldMinQ = Double.NaN; + Double oldP = Double.NaN; + if (oldPoint != null) { + oldMaxQ = oldPoint.getMaxQ(); + oldMinQ = oldPoint.getMinQ(); + oldP = oldPoint.getP(); + } + var maxQ = newPoint.getMaxQ() != null ? newPoint.getMaxQ() : oldMaxQ; + var minQ = newPoint.getMinQ() != null ? newPoint.getMinQ() : oldMinQ; + var p = newPoint.getP() != null ? newPoint.getP() : oldP; + + adder.beginPoint() + .setMaxQ(maxQ) + .setMinQ(minQ) + .setP(p) + .endPoint(); + addToReports(reports, p, oldP, "P" + fieldSuffix); + addToReports(reports, minQ, oldMinQ, "QminP" + fieldSuffix); + addToReports(reports, maxQ, oldMaxQ, "QmaxP" + fieldSuffix); + } + + public void addToReports(List reports, Double newValue, Double oldValue, String fieldName) { + if (newValue != null) { + reports.add(buildModificationReport(oldValue, newValue, fieldName)); + } + } + + public void modifyMinMaxReactiveLimits(AttributeModification minimumReactivePower, AttributeModification maximumReactivePower, ReactiveLimitsHolder reactiveLimitsHolder, + ReportNode subReportNode, ReportNode subReportNodeLimits) { + MinMaxReactiveLimits minMaxReactiveLimits = null; + ReactiveLimits reactiveLimits = reactiveLimitsHolder.getReactiveLimits(); + MinMaxReactiveLimitsAdder newMinMaxReactiveLimitsAdder = reactiveLimitsHolder.newMinMaxReactiveLimits(); + if (reactiveLimits != null) { + ReactiveLimitsKind limitsKind = reactiveLimits.getKind(); + if (limitsKind == ReactiveLimitsKind.MIN_MAX) { + minMaxReactiveLimits = reactiveLimitsHolder.getReactiveLimits(MinMaxReactiveLimitsImpl.class); + } + } + modifyMinMaxReactiveLimits(minMaxReactiveLimits, + newMinMaxReactiveLimitsAdder, subReportNode, subReportNodeLimits, + minimumReactivePower, + maximumReactivePower); + } + + public void modifyMinMaxReactiveLimits(MinMaxReactiveLimits minMaxReactiveLimits, MinMaxReactiveLimitsAdder newMinMaxReactiveLimits, + ReportNode subReportNode, ReportNode subReportNodeLimits, AttributeModification minimumReactivePower, AttributeModification maximumReactivePower) { + List reports = new ArrayList<>(); + + if (minimumReactivePower != null + && maximumReactivePower != null) { + newMinMaxReactiveLimits.setMinQ(minimumReactivePower.getValue()) + .setMaxQ(maximumReactivePower.getValue()) + .add(); + reports.add(ModificationUtils.getInstance().buildModificationReport(minMaxReactiveLimits != null ? minMaxReactiveLimits.getMinQ() : Double.NaN, + minimumReactivePower.getValue(), + MIN_REACTIVE_POWER_FIELDNAME)); + reports.add(ModificationUtils.getInstance().buildModificationReport(minMaxReactiveLimits != null ? minMaxReactiveLimits.getMaxQ() : Double.NaN, + maximumReactivePower.getValue(), + MAX_REACTIVE_POWER_FIELDNAME)); + } else if (minimumReactivePower != null) { + newMinMaxReactiveLimits.setMinQ(minimumReactivePower.getValue()) + .setMaxQ(minMaxReactiveLimits != null ? minMaxReactiveLimits.getMaxQ() : Double.MAX_VALUE) + .add(); + reports.add(ModificationUtils.getInstance().buildModificationReport(minMaxReactiveLimits != null ? minMaxReactiveLimits.getMinQ() : Double.NaN, + minimumReactivePower.getValue(), + MIN_REACTIVE_POWER_FIELDNAME)); + } else if (maximumReactivePower != null) { + newMinMaxReactiveLimits + .setMinQ(minMaxReactiveLimits != null ? minMaxReactiveLimits.getMinQ() : -Double.MAX_VALUE) + .setMaxQ(maximumReactivePower.getValue()) + .add(); + reports.add(ModificationUtils.getInstance().buildModificationReport(minMaxReactiveLimits != null ? minMaxReactiveLimits.getMaxQ() : Double.NaN, + maximumReactivePower.getValue(), + MAX_REACTIVE_POWER_FIELDNAME)); + } else if (minMaxReactiveLimits == null) { + newMinMaxReactiveLimits.setMinQ(-Double.MAX_VALUE) + .setMaxQ(Double.MAX_VALUE) + .add(); + reports.add(ModificationUtils.getInstance().buildModificationReport(Double.NaN, + -Double.MAX_VALUE, + MIN_REACTIVE_POWER_FIELDNAME)); + reports.add(ModificationUtils.getInstance().buildModificationReport(Double.NaN, + Double.MAX_VALUE, + MAX_REACTIVE_POWER_FIELDNAME)); + } + ReportNode subReportNodeReactiveLimits = null; + ReportNode subReportNodeLimits2 = subReportNodeLimits; + if (subReportNodeLimits == null && !reports.isEmpty()) { + subReportNodeLimits2 = subReportNode.newReportNode().withMessageTemplate(LIMITS, LIMITS).add(); + } + if (subReportNodeLimits2 != null && !reports.isEmpty()) { + subReportNodeReactiveLimits = subReportNodeLimits2.newReportNode().withMessageTemplate(REACTIVE_LIMITS, REACTIVE_LIMITS).add(); + } + reportModifications(subReportNodeReactiveLimits, reports, "minMaxReactiveLimitsModified", "By range"); + } + + private void modifyExistingActivePowerControl(ActivePowerControl activePowerControl, + AttributeModification participateInfo, + AttributeModification droopInfo, + List reports) { + double oldDroop = activePowerControl.getDroop(); + boolean oldParticipate = activePowerControl.isParticipate(); + + Optional.ofNullable(participateInfo).ifPresent(info -> { + activePowerControl.setParticipate(info.getValue()); + if (reports != null) { + reports.add(buildModificationReport(oldParticipate, info.getValue(), "Participate")); + } + }); + + Optional.ofNullable(droopInfo).ifPresent(info -> { + activePowerControl.setDroop(info.getValue()); + if (reports != null) { + reports.add(buildModificationReport(oldDroop, info.getValue(), "Droop")); + } + }); + } + + private void createNewActivePowerControl(ActivePowerControlAdder adder, + AttributeModification participateInfo, + AttributeModification droopInfo, + List reports, + NetworkModificationException.Type exceptionType, + String errorMessage) { + Boolean participate = Optional.ofNullable(participateInfo).map(AttributeModification::getValue).orElse(null); + Float droop = Optional.ofNullable(droopInfo).map(AttributeModification::getValue).orElse(null); + checkActivePowerControl(participate, droop, exceptionType, errorMessage); + if (participate != null && droop != null) { + adder.withParticipate(participate) + .withDroop(droop) + .add(); + if (reports != null) { + reports.add(buildModificationReport(null, participate, "Participate")); + reports.add(buildModificationReport(Double.NaN, droop, "Droop")); + } + } + } + + public void checkActivePowerControl(Boolean participate, Float droop, NetworkModificationException.Type exceptionType, String errorMessage) { + if (Boolean.TRUE.equals(participate) && droop == null) { + throw new NetworkModificationException(exceptionType, String.format("%s Active power regulation on : missing required droop value", errorMessage)); + } + } + + public ReportNode modifyActivePowerControlAttributes(ActivePowerControl activePowerControl, + ActivePowerControlAdder activePowerControlAdder, + AttributeModification participateInfo, + AttributeModification droopInfo, + ReportNode subReportNode, + ReportNode subReporterSetpoints, + NetworkModificationException.Type exceptionType, + String errorMessage) { + List reports = new ArrayList<>(); + if (activePowerControl != null) { + modifyExistingActivePowerControl(activePowerControl, participateInfo, droopInfo, reports); + } else { + createNewActivePowerControl(activePowerControlAdder, participateInfo, droopInfo, reports, exceptionType, errorMessage); + } + if (subReportNode != null) { + ReportNode subReportNodeSetpoints2 = subReporterSetpoints; + if (subReporterSetpoints == null && !reports.isEmpty()) { + subReportNodeSetpoints2 = subReportNode.newReportNode().withMessageTemplate(SETPOINTS, SETPOINTS).add(); + } + reportModifications(subReportNodeSetpoints2, reports, "activePowerControlModified", "Active power control"); + return subReportNodeSetpoints2; + } + return null; + } + + public void checkMaxQGreaterThanMinQ( + List modificationPoints, + NetworkModificationException.Type exceptionType, String errorMessage + ) { + for (var point : modificationPoints) { + double maxQ = Double.NaN; + double minQ = Double.NaN; + + if (point.getMaxQ() != null) { + maxQ = point.getMaxQ(); + } else if (point.getOldMaxQ() != null) { + maxQ = point.getOldMaxQ(); + } + + if (point.getMinQ() != null) { + minQ = point.getMinQ(); + } else if (point.getOldMinQ() != null) { + minQ = point.getOldMinQ(); + } + + if (maxQ < minQ) { + throw new NetworkModificationException( + exceptionType, + errorMessage + "maximum reactive power " + maxQ + " is expected to be greater than or equal to minimum reactive power " + minQ + ); + } + } + } + + public void checkMaxReactivePowerGreaterThanMinReactivePower(MinMaxReactiveLimits minMaxReactiveLimits, AttributeModification minimumReactivePowerInfo, AttributeModification maximumReactivePowerInfo, NetworkModificationException.Type exceptionType, String errorMessage) { + double previousMinimumReactivePower = minMaxReactiveLimits.getMinQ(); + double previousMaximumReactivePower = minMaxReactiveLimits.getMaxQ(); + double minReactivePower = minimumReactivePowerInfo != null ? minimumReactivePowerInfo.getValue() : previousMinimumReactivePower; + double maxReactivePower = maximumReactivePowerInfo != null ? maximumReactivePowerInfo.getValue() : previousMaximumReactivePower; + if (minReactivePower > maxReactivePower) { + throw new NetworkModificationException(exceptionType, errorMessage + "maximum reactive power " + maxReactivePower + " is expected to be greater than or equal to minimum reactive power " + minReactivePower); + } + } + + public void checkReactiveLimit(ReactiveLimitsHolder reactiveLimitsHolder, AttributeModification minimumReactivePower, AttributeModification maximumReactivePower, + List modificationPoints, NetworkModificationException.Type exeptionType, String errorMessage) { + if (reactiveLimitsHolder.getReactiveLimits().getKind() == ReactiveLimitsKind.MIN_MAX + && (minimumReactivePower != null || maximumReactivePower != null)) { + MinMaxReactiveLimits minMaxReactiveLimits = reactiveLimitsHolder.getReactiveLimits(MinMaxReactiveLimits.class); + ModificationUtils.getInstance().checkMaxReactivePowerGreaterThanMinReactivePower(minMaxReactiveLimits, minimumReactivePower, maximumReactivePower, exeptionType, errorMessage); + } + if (modificationPoints != null) { + ModificationUtils.getInstance().checkMaxQGreaterThanMinQ(modificationPoints, exeptionType, errorMessage); + } + } + + public void checkActivePowerZeroOrBetweenMinAndMaxActivePower(AttributeModification activePowerInfos, AttributeModification minActivePowerInfos, AttributeModification maxActivePowerInfos, Double previousMinActivePower, Double previousMaxActivePower, Double previousActivePower, NetworkModificationException.Type exceptionType, String errorMessage) { + Double minActivePower = minActivePowerInfos != null ? minActivePowerInfos.getValue() : previousMinActivePower; + Double maxActivePower = maxActivePowerInfos != null ? maxActivePowerInfos.getValue() : previousMaxActivePower; + Double activePower = activePowerInfos != null ? activePowerInfos.getValue() : previousActivePower; + + if (activePower != 0 && (activePower < minActivePower || activePower > maxActivePower)) { + throw new NetworkModificationException(exceptionType, errorMessage + "Active power " + activePower + " is expected to be equal to 0 or within the range of minimum active power and maximum active power: [" + minActivePower + ", " + maxActivePower + "]"); + } + } + + private NetworkModificationException makeEquipmentException(NetworkModificationException.Type errorType, + String equipmentId, + String equipmentName, + String msgSuffix) { + return new NetworkModificationException(errorType, + equipmentName + " '" + equipmentId + "' : " + msgSuffix); + } + + public void checkReactiveLimitsCreation(ReactiveLimitsHolderInfos modificationInfos, + NetworkModificationException.Type errorType, + String equipmentId, + String equipmentName) { + // check min max reactive limits + if (modificationInfos.getMinQ() != null && modificationInfos.getMaxQ() != null) { + if (Double.isNaN(modificationInfos.getMinQ())) { + throw makeEquipmentException(errorType, equipmentId, equipmentName, "minimum reactive power is not set"); + } else if (Double.isNaN(modificationInfos.getMaxQ())) { + throw makeEquipmentException(errorType, equipmentId, equipmentName, "maximum reactive power is not set"); + } else if (modificationInfos.getMaxQ() < modificationInfos.getMinQ()) { + throw makeEquipmentException(errorType, equipmentId, equipmentName, "maximum reactive power is expected to be greater than or equal to minimum reactive power"); + } + } + + // check reactive capability curve limits + List points = modificationInfos.getReactiveCapabilityCurvePoints(); + if (!org.apache.commons.collections4.CollectionUtils.isEmpty(points)) { + if (points.size() < 2) { + throw makeEquipmentException(errorType, equipmentId, equipmentName, "a reactive capability curve should have at least two points"); + } + IntStream.range(0, points.size()) + .forEach(i -> { + ReactiveCapabilityCurveCreationInfos newPoint = points.get(i); + if (Double.isNaN(newPoint.getP())) { + throw makeEquipmentException(errorType, equipmentId, equipmentName, "P is not set in a reactive capability curve limits point"); + } else if (Double.isNaN(newPoint.getMinQ())) { + throw makeEquipmentException(errorType, equipmentId, equipmentName, "min Q is not set in a reactive capability curve limits point"); + } else if (Double.isNaN(newPoint.getMaxQ())) { + throw makeEquipmentException(errorType, equipmentId, equipmentName, "max Q is not set in a reactive capability curve limits point"); + } + }); + } + } + + public void checkReactivePowerLimitsAndSetPointsCreation(StaticVarCompensatorCreationInfos creationInfos) { + String equipmentName = "StaticVarCompensator"; + // check min max reactive limits + if (Objects.isNull(creationInfos.getMinSusceptance()) && Objects.isNull(creationInfos.getMinQAtNominalV())) { + throw makeEquipmentException(creationInfos.getErrorType(), creationInfos.getEquipmentId(), equipmentName, "minimum susceptance is not set"); + } + if (Objects.isNull(creationInfos.getMaxSusceptance()) && Objects.isNull(creationInfos.getMaxQAtNominalV())) { + throw makeEquipmentException(creationInfos.getErrorType(), creationInfos.getEquipmentId(), equipmentName, "maximum susceptance is not set"); + } + if (Objects.nonNull(creationInfos.getMaxSusceptance()) && Objects.nonNull(creationInfos.getMinSusceptance()) && creationInfos.getMaxSusceptance() < creationInfos.getMinSusceptance()) { + throw makeEquipmentException(creationInfos.getErrorType(), creationInfos.getEquipmentId(), equipmentName, "maximum susceptance is expected to be greater than or equal to minimum susceptance"); + } + if (Objects.nonNull(creationInfos.getMaxQAtNominalV()) && Objects.nonNull(creationInfos.getMinQAtNominalV()) && creationInfos.getMaxQAtNominalV() < creationInfos.getMinQAtNominalV()) { + throw makeEquipmentException(creationInfos.getErrorType(), creationInfos.getEquipmentId(), equipmentName, "maximum Q at nominal voltage is expected to be greater than or equal to minimum Q"); + } + + // check set points + if (Objects.requireNonNull(creationInfos.getRegulationMode()) == StaticVarCompensator.RegulationMode.VOLTAGE && creationInfos.getVoltageSetpoint() == null) { + throw makeEquipmentException(creationInfos.getErrorType(), creationInfos.getEquipmentId(), equipmentName, "Voltage setpoint is not set"); + } + if (creationInfos.getRegulationMode() == StaticVarCompensator.RegulationMode.REACTIVE_POWER && creationInfos.getReactivePowerSetpoint() == null) { + throw makeEquipmentException(creationInfos.getErrorType(), creationInfos.getEquipmentId(), equipmentName, "Reactive power setpoint is not set"); + } + } + + public void checkStandbyAutomatonCreation(StaticVarCompensatorCreationInfos creationInfos) { + String equipmentName = "StaticVarCompensator"; + if (Boolean.TRUE.equals(creationInfos.isStandby()) && creationInfos.getRegulationMode() != StaticVarCompensator.RegulationMode.VOLTAGE) { + throw makeEquipmentException(creationInfos.getErrorType(), creationInfos.getEquipmentId(), equipmentName, "Standby is only supported in Voltage Regulation mode"); + } + if (Objects.nonNull(creationInfos.getB0()) && Objects.nonNull(creationInfos.getMinSusceptance()) && Objects.nonNull(creationInfos.getMaxSusceptance()) && + (creationInfos.getB0() < creationInfos.getMinSusceptance() || creationInfos.getB0() > creationInfos.getMaxSusceptance())) { + throw makeEquipmentException(creationInfos.getErrorType(), creationInfos.getEquipmentId(), equipmentName, + "b0 must be within the range of minimum susceptance and maximum susceptance"); + } + if (Objects.nonNull(creationInfos.getQ0()) && Objects.nonNull(creationInfos.getMinQAtNominalV()) && Objects.nonNull(creationInfos.getMaxQAtNominalV()) && + (creationInfos.getQ0() < creationInfos.getMinQAtNominalV() || creationInfos.getQ0() > creationInfos.getMaxQAtNominalV())) { + throw makeEquipmentException(creationInfos.getErrorType(), creationInfos.getEquipmentId(), equipmentName, + "q0 must be within the range of minimum Q and maximum Q"); + } + } + + public static void addToReports(List reports, Double newValue, String fieldName) { + if (newValue != null) { + reports.add(ModificationUtils.getInstance().buildCreationReport(newValue, fieldName)); + } + } + + public void createReactiveLimits(ReactiveLimitsHolderInfos creationInfos, + ReactiveLimitsHolder reactiveLimitsHolder, + ReportNode subReporter) { + if (Boolean.TRUE.equals(creationInfos.getReactiveCapabilityCurve())) { + createReactiveCapabilityCurve(creationInfos, reactiveLimitsHolder, subReporter); + } else if (Boolean.FALSE.equals(creationInfos.getReactiveCapabilityCurve())) { + createMinMaxReactiveLimits(creationInfos, reactiveLimitsHolder, subReporter); + } + } + + public void createMinMaxReactiveLimits(ReactiveLimitsHolderInfos batteryCreationInfos, + ReactiveLimitsHolder reactiveLimitsHolder, + ReportNode subReportNode) { + List minMaxReactiveLimitsReports = new ArrayList<>(); + if (batteryCreationInfos.getMinQ() != null && batteryCreationInfos.getMaxQ() != null) { + reactiveLimitsHolder.newMinMaxReactiveLimits() + .setMinQ(batteryCreationInfos.getMinQ()) + .setMaxQ(batteryCreationInfos.getMaxQ()) + .add(); + + minMaxReactiveLimitsReports.add(ModificationUtils.getInstance().buildCreationReport( + batteryCreationInfos.getMinQ(), + MIN_REACTIVE_POWER_FIELDNAME)); + + minMaxReactiveLimitsReports.add(ModificationUtils.getInstance().buildCreationReport( + batteryCreationInfos.getMaxQ(), + MAX_REACTIVE_POWER_FIELDNAME)); + + ReportNode subReporterReactiveLimits = subReportNode.newReportNode().withMessageTemplate(REACTIVE_LIMITS, REACTIVE_LIMITS).add(); + + ModificationUtils.getInstance().reportModifications(subReporterReactiveLimits, minMaxReactiveLimitsReports, "minMaxReactiveLimitsCreated", "By range"); + } + } + + public void createReactiveCapabilityCurve(ReactiveLimitsHolderInfos creationInfos, + ReactiveLimitsHolder reactiveLimitsHolder, + ReportNode subReportNode) { + List pointsReports = new ArrayList<>(); + ReactiveCapabilityCurveAdder adder = reactiveLimitsHolder.newReactiveCapabilityCurve(); + List points = creationInfos.getReactiveCapabilityCurvePoints(); + IntStream.range(0, points.size()) + .forEach(i -> { + String fieldSuffix; + ReactiveCapabilityCurveCreationInfos newPoint = points.get(i); + if (i == 0) { + fieldSuffix = "min"; + } else if (i == (points.size() - 1)) { + fieldSuffix = "max"; + } else { + fieldSuffix = Integer.toString(i - 1); + } + createReactiveCapabilityCurvePoint(adder, newPoint, pointsReports, fieldSuffix); + }); + adder.add(); + ReportNode subReporterReactiveLimits = subReportNode.newReportNode().withMessageTemplate(REACTIVE_LIMITS, REACTIVE_LIMITS).add(); + ModificationUtils.getInstance().reportModifications(subReporterReactiveLimits, pointsReports, "curveReactiveLimitsCreated", "By diagram"); + } + + private void createReactiveCapabilityCurvePoint(ReactiveCapabilityCurveAdder adder, + ReactiveCapabilityCurveCreationInfos point, + List reports, + String fieldSuffix) { + adder.beginPoint() + .setMaxQ(point.getMaxQ()) + .setMinQ(point.getMinQ()) + .setP(point.getP()) + .endPoint(); + addToReports(reports, point.getP(), "P" + fieldSuffix); + addToReports(reports, point.getMinQ(), "QminP" + fieldSuffix); + addToReports(reports, point.getMaxQ(), "QmaxP" + fieldSuffix); + } + + public boolean isValidFilter(ReportNode subReportNode, + NetworkModificationException.Type errorType, + Map exportFilters) { + boolean noValidEquipmentId = exportFilters.values().stream() + .allMatch(filterEquipments -> CollectionUtils.isEmpty(filterEquipments.getIdentifiableAttributes())); + + if (noValidEquipmentId) { + String errorMsg = "${errorType}: There is no valid equipment ID among the provided filter(s)"; + createReport(subReportNode, "invalidFilters", errorMsg, Map.of("errorType", errorType), TypedValue.ERROR_SEVERITY); + return false; + } + + return true; + } + + public static List getIdentifiableAttributes(Map exportFilters, Map filtersWithWrongEquipmentIds, List filterInfos, ReportNode subReportNode) { + filterInfos.stream() + .filter(f -> !exportFilters.containsKey(f.getId())) + .forEach(f -> createReport(subReportNode, + "filterNotFound", + "Cannot find the following filter: ${name}", + Map.of("name", f.getName()), TypedValue.WARN_SEVERITY)); + + return filterInfos + .stream() + .filter(f -> !filtersWithWrongEquipmentIds.containsKey(f.getId()) && exportFilters.containsKey(f.getId())) + .flatMap(f -> exportFilters.get(f.getId()) + .getIdentifiableAttributes() + .stream()) + .toList(); + } + + @Nullable + public static Map getUuidFilterEquipmentsMap(IFilterService filterService, Network network, ReportNode subReportNode, Map filters, NetworkModificationException.Type errorType) { + Map exportFilters = filterService.getUuidFilterEquipmentsMap(network, filters); + + boolean isValidFilter = ModificationUtils.getInstance().isValidFilter(subReportNode, errorType, exportFilters); + return isValidFilter ? exportFilters : null; + } + + public static Map getUuidFilterWrongEquipmentsIdsMap(ReportNode subReportNode, Map exportFilters, Map filters) { + // collect all filters with wrong equipments ids + Map filterWithWrongEquipmentsIds = exportFilters.entrySet().stream() + .filter(e -> !CollectionUtils.isEmpty(e.getValue().getNotFoundEquipments())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + // create report for each wrong filter + filterWithWrongEquipmentsIds.values().forEach(f -> { + var equipmentIds = String.join(", ", f.getNotFoundEquipments()); + createReport(subReportNode, + "filterEquipmentsNotFound_" + f.getFilterName(), + "Cannot find the following equipments ${equipmentIds} in filter ${filters}", + Map.of("equipmentIds", equipmentIds, "filters", filters.get(f.getFilterId())), TypedValue.WARN_SEVERITY); + }); + return filterWithWrongEquipmentsIds; + } + + public static void insertReportNode(ReportNode parent, ReportNode child) { + ReportNodeAdder adder = parent.newReportNode().withMessageTemplate(child.getMessageKey(), child.getMessageTemplate()); + for (Map.Entry valueEntry : child.getValues().entrySet()) { + adder.withUntypedValue(valueEntry.getKey(), valueEntry.getValue().toString()); + } + child.getValue(ReportConstants.SEVERITY_KEY).ifPresent(adder::withSeverity); + ReportNode insertedChild = adder.add(); + if (child.getChildren() != null) { + child.getChildren().forEach(grandChild -> insertReportNode(insertedChild, grandChild)); + } + } + + public static void createInjectionInNodeBreaker(VoltageLevel voltageLevel, InjectionCreationInfos injectionCreationInfos, + Network network, InjectionAdder injectionAdder, ReportNode subReportNode) { + int position = ModificationUtils.getInstance().getPosition(injectionCreationInfos.getConnectionPosition(), + injectionCreationInfos.getBusOrBusbarSectionId(), network, voltageLevel); + CreateFeederBay algo = new CreateFeederBayBuilder() + .withBusOrBusbarSectionId(injectionCreationInfos.getBusOrBusbarSectionId()) + .withInjectionDirection(injectionCreationInfos.getConnectionDirection()) + .withInjectionFeederName(injectionCreationInfos.getConnectionName() != null + ? injectionCreationInfos.getConnectionName() + : injectionCreationInfos.getEquipmentId()) + .withInjectionPositionOrder(position) + .withInjectionAdder(injectionAdder) + .build(); + algo.apply(network, true, subReportNode); + } + + public static void reportInjectionCreationConnectivity(InjectionCreationInfos injectionCreationInfos, ReportNode subReporter) { + if (Objects.isNull(injectionCreationInfos.getVoltageLevelId()) || Objects.isNull(injectionCreationInfos.getBusOrBusbarSectionId())) { + return; + } + + if (injectionCreationInfos.getConnectionName() != null || + injectionCreationInfos.getConnectionDirection() != null || + injectionCreationInfos.getConnectionPosition() != null) { + List connectivityReports = new ArrayList<>(); + if (injectionCreationInfos.getConnectionName() != null) { + connectivityReports.add(ModificationUtils.getInstance() + .buildCreationReport(injectionCreationInfos.getConnectionName(), CONNECTION_NAME_FIELD_NAME)); + } + if (injectionCreationInfos.getConnectionDirection() != null) { + connectivityReports.add(ModificationUtils.getInstance() + .buildCreationReport(injectionCreationInfos.getConnectionDirection(), CONNECTION_DIRECTION_FIELD_NAME)); + } + if (injectionCreationInfos.getConnectionPosition() != null) { + connectivityReports.add(ModificationUtils.getInstance() + .buildCreationReport(injectionCreationInfos.getConnectionPosition(), CONNECTION_POSITION_FIELD_NAME)); + } + if (!injectionCreationInfos.isTerminalConnected()) { + connectivityReports.add(ReportNode.newRootReportNode() + .withMessageTemplate(EQUIPMENT_DISCONNECTED, " Equipment with id=${id} disconnected") + .withUntypedValue("id", injectionCreationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + } + ModificationUtils.getInstance().reportModifications(subReporter, connectivityReports, "ConnectivityCreated", CONNECTIVITY); + } + } +} + diff --git a/src/main/java/org/gridsuite/modification/utils/PropertiesUtils.java b/src/main/java/org/gridsuite/modification/utils/PropertiesUtils.java new file mode 100644 index 0000000..1baa1c3 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/utils/PropertiesUtils.java @@ -0,0 +1,71 @@ +package org.gridsuite.modification.utils; + +import com.powsybl.commons.report.*; +import com.powsybl.iidm.network.Identifiable; +import org.gridsuite.modification.dto.FreePropertyInfos; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public final class PropertiesUtils { + public static final String PROPERTIES = "Properties"; + + private PropertiesUtils() { + // Should not be instantiated + } + + public static void applyProperties(Identifiable identifiable, ReportNode subReportNode, @Nullable List properties, String propertiesLabelKey) { + List reportNodes = new ArrayList<>(); + Optional.ofNullable(properties).ifPresent(props -> + props.forEach(prop -> + Optional.ofNullable(PropertiesUtils.applyProperty(identifiable, prop)) + .ifPresent(reportNodes::add) + ) + ); + if (!reportNodes.isEmpty()) { + ModificationUtils.getInstance().reportModifications(subReportNode, reportNodes, + propertiesLabelKey, PROPERTIES); + } + } + + private static ReportNode applyProperty(Identifiable identifiable, FreePropertyInfos prop) { + ReportNodeBuilder builder = ReportNode.newRootReportNode(); + if (prop.isDeletionMark()) { + if (identifiable.removeProperty(prop.getName())) { + reportPropertyDeletion(builder, prop); + return builder.build(); + } + } else { + String oldValue = identifiable.setProperty(prop.getName(), prop.getValue()); + if (oldValue != null) { // update + reportPropertyModification(builder, prop); + return builder.build(); + } else { // insert + reportPropertyCreation(builder, prop); + return builder.build(); + } + } + return null; + } + + private static void reportPropertyCreation(ReportNodeAdderOrBuilder adderOrBuilder, FreePropertyInfos prop) { + adderOrBuilder.withMessageTemplate("propertyAdded", " Property ${name} added with value ${value}") + .withUntypedValue("name", prop.getName()) + .withUntypedValue("value", prop.getValue()) + .withSeverity(TypedValue.INFO_SEVERITY); + } + + private static void reportPropertyModification(ReportNodeAdderOrBuilder adderOrBuilder, FreePropertyInfos prop) { + adderOrBuilder.withMessageTemplate("propertyChanged", " Property ${name} changed : ${from} -> ${to}") + .withUntypedValue("name", prop.getName()) + .withUntypedValue("to", prop.getValue()) + .withUntypedValue("from", prop.getPreviousValue() == null ? "null" : prop.getPreviousValue()) + .withSeverity(TypedValue.INFO_SEVERITY); + } + + private static void reportPropertyDeletion(ReportNodeAdderOrBuilder adderOrBuilder, FreePropertyInfos prop) { + adderOrBuilder.withMessageTemplate("propertyDeleted", " Property ${name} deleted") + .withUntypedValue("name", prop.getName()) + .withSeverity(TypedValue.INFO_SEVERITY); + } +} diff --git a/src/test/java/org/gridsuite/modification/modifications/BatteryModificationTest.java b/src/test/java/org/gridsuite/modification/modifications/BatteryModificationTest.java new file mode 100644 index 0000000..73d2cc2 --- /dev/null +++ b/src/test/java/org/gridsuite/modification/modifications/BatteryModificationTest.java @@ -0,0 +1,392 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.modifications; + +import com.powsybl.iidm.network.Battery; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ReactiveCapabilityCurve; +import com.powsybl.iidm.network.ReactiveLimitsKind; +import com.powsybl.iidm.network.extensions.ActivePowerControl; + +import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.*; +import org.gridsuite.modification.utils.NetworkCreation; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.stream.IntStream; + +import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Tag("IntegrationTest") +class BatteryModificationTest { + + private Network network; + + @BeforeEach + public void setUp() { + network = NetworkCreation.create(UUID.randomUUID(), true); + } + + @AfterEach + public void tearOff() { + } + + @Test + public void testApply() throws Exception { + buildModification().toModification().apply(network); + assertAfterNetworkModificationApplication(); + } + + @Test + public void testCheck() { + BatteryModificationInfos batteryModificationInfos = (BatteryModificationInfos) buildModification(); + batteryModificationInfos.setTargetP(new AttributeModification<>(-1.0, OperationType.SET)); + assertThrows(NetworkModificationException.class, () -> batteryModificationInfos.toModification().check(network)); + } + + private static final String PROPERTY_NAME = "property-name"; + private static final String PROPERTY_VALUE = "property-value"; + + protected Network createNetwork(UUID networkUuid) { + return NetworkCreation.create(networkUuid, true); + } + + protected BatteryModificationInfos buildModification() { + return BatteryModificationInfos.builder() + .stashed(false) + .equipmentId("v3Battery") + .equipmentName(new AttributeModification<>("newV1Battery", OperationType.SET)) + .voltageLevelId(new AttributeModification<>("v2", OperationType.SET)) + .busOrBusbarSectionId(new AttributeModification<>("1B", OperationType.SET)) + .targetP(new AttributeModification<>(80.0, OperationType.SET)) + .targetQ(new AttributeModification<>(40.0, OperationType.SET)) + .minP(new AttributeModification<>(0., OperationType.SET)) + .maxP(new AttributeModification<>(100., OperationType.SET)) + .minQ(new AttributeModification<>(-100., OperationType.SET)) + .maxP(new AttributeModification<>(100., OperationType.SET)) + .reactiveCapabilityCurvePoints(List.of( + new ReactiveCapabilityCurveModificationInfos(0., 0., 100., 100., 0., + 0.1), + new ReactiveCapabilityCurveModificationInfos(0., 0., 100., 100., 200., + 150.))) + .droop(new AttributeModification<>(0.1f, OperationType.SET)) + .participate(new AttributeModification<>(true, OperationType.SET)) + .reactiveCapabilityCurve(new AttributeModification<>(true, OperationType.SET)) + .properties(List.of(FreePropertyInfos.builder().name(PROPERTY_NAME) + .value(PROPERTY_VALUE).build())) + .build(); + } + + protected void assertAfterNetworkModificationApplication() { + Battery modifiedBattery = network.getBattery("v3Battery"); + BatteryModificationInfos batteryModificationInfos = (BatteryModificationInfos) buildModification(); + assertEquals("newV1Battery", modifiedBattery.getNameOrId()); + assertEquals(80.0, modifiedBattery.getTargetP()); + assertEquals(40.0, modifiedBattery.getTargetQ()); + assertEquals(0., modifiedBattery.getMinP()); + assertEquals(100., modifiedBattery.getMaxP()); + assertEquals(0.1f, modifiedBattery.getExtension(ActivePowerControl.class).getDroop()); + assertTrue(modifiedBattery.getExtension(ActivePowerControl.class).isParticipate()); + assertEquals(ReactiveLimitsKind.CURVE, modifiedBattery.getReactiveLimits().getKind()); + Collection points = modifiedBattery + .getReactiveLimits(ReactiveCapabilityCurve.class).getPoints(); + List batteryPoints = new ArrayList<>(points); + List modificationPoints = batteryModificationInfos + .getReactiveCapabilityCurvePoints(); + if (!CollectionUtils.isEmpty(points)) { + IntStream.range(0, batteryPoints.size()) + .forEach(i -> { + var point = batteryPoints.get(i); + var modificationPoint = modificationPoints.get(i); + assertEquals(modificationPoint.getMaxQ(), point.getMaxQ()); + assertEquals(modificationPoint.getMinQ(), point.getMinQ()); + assertEquals(modificationPoint.getP(), point.getP()); + }); + } + assertEquals(PROPERTY_VALUE, network.getBattery("v3Battery").getProperty(PROPERTY_NAME)); + } + + // @Test + // void testMinMaxReactiveLimitsAttributesModification() throws Exception { + // BatteryModificationInfos batteryModificationInfos = (BatteryModificationInfos) buildModification(); + + // // setting ReactiveCapabilityCurve to false with null min and max reactive + // // limits + // batteryModificationInfos + // .setReactiveCapabilityCurve(new AttributeModification<>(false, OperationType.SET)); + // batteryModificationInfos.setMaxQ(null); + // batteryModificationInfos.setMinQ(null); + // // setting ReactiveCapabilityCurvePoints for the battery we are modifying + // Battery battery = network.getBattery("v3Battery"); + // battery.newReactiveCapabilityCurve() + // .beginPoint() + // .setP(0.) + // .setMaxQ(100.) + // .setMinQ(0.) + // .endPoint() + // .beginPoint() + // .setP(200.) + // .setMaxQ(150.) + // .setMinQ(0.) + // .endPoint() + // .add(); + // String modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + + // mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + + // BatteryModificationInfos createdModification = (BatteryModificationInfos) modificationRepository + // .getModifications(getGroupId(), false, true).get(0); + + // assertThat(createdModification).recursivelyEquals(batteryModificationInfos); + // testNetworkModificationsCount(getGroupId(), 1); + + // // Modifying only min reactive limit + // batteryModificationInfos.setMinQ(new AttributeModification<>(-200., OperationType.SET)); + // modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + + // mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + + // createdModification = (BatteryModificationInfos) modificationRepository + // .getModifications(getGroupId(), false, true).get(1); + + // assertThat(createdModification).recursivelyEquals(batteryModificationInfos); + // testNetworkModificationsCount(getGroupId(), 2); + + // // Modifying only max reactive limit + // batteryModificationInfos.setMinQ(null); + // batteryModificationInfos.setMaxQ(new AttributeModification<>(200., OperationType.SET)); + // modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + + // mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + + // createdModification = (BatteryModificationInfos) modificationRepository + // .getModifications(getGroupId(), false, true).get(2); + + // assertThat(createdModification).recursivelyEquals(batteryModificationInfos); + // testNetworkModificationsCount(getGroupId(), 3); + + // // Modifying both min and max reactive limits + // batteryModificationInfos.setMinQ(new AttributeModification<>(-1.1, OperationType.SET)); + // modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + + // mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + + // createdModification = (BatteryModificationInfos) modificationRepository + // .getModifications(getGroupId(), false, true).get(3); + + // assertThat(createdModification).recursivelyEquals(batteryModificationInfos); + // testNetworkModificationsCount(getGroupId(), 4); + + // // nothing before reactive limits modification + // batteryModificationInfos = (BatteryModificationInfos) buildModification(); + // batteryModificationInfos.setEquipmentName(null); + // batteryModificationInfos.setMinP(null); + // batteryModificationInfos.setMaxP(null); + // modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + // mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + // createdModification = (BatteryModificationInfos) modificationRepository + // .getModifications(getGroupId(), false, true).get(4); + // assertThat(createdModification).recursivelyEquals(batteryModificationInfos); + // testNetworkModificationsCount(getGroupId(), 5); + // } + + // @Test + // void testDroopUnchanged() throws Exception { + // BatteryModificationInfos batteryModificationInfos = (BatteryModificationInfos) buildModification(); + + // batteryModificationInfos.getDroop().setValue(18f); + // String modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + + // mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + + // BatteryModificationInfos createdModification = (BatteryModificationInfos) modificationRepository + // .getModifications(getGroupId(), false, true).get(0); + + // assertThat(createdModification).recursivelyEquals(batteryModificationInfos); + + // // setting droop to null, modifying only participate + // batteryModificationInfos.setDroop(null); + // modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + + // mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + + // createdModification = (BatteryModificationInfos) modificationRepository + // .getModifications(getGroupId(), false, true).get(0); + + // assertEquals(18f, createdModification.getDroop().getValue()); + // } + + // @Test + // void testImpactsAfterActivePowerControlModifications() throws Exception { + // BatteryModificationInfos batteryModificationInfos = (BatteryModificationInfos) buildModification(); + // String modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + // mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + // Battery battery = getNetwork().getBattery("v3Battery"); + // assertEquals(0.1f, battery.getExtension(ActivePowerControl.class).getDroop()); + // assertTrue(battery.getExtension(ActivePowerControl.class).isParticipate()); + // // modify only droop + // batteryModificationInfos.setDroop(new AttributeModification<>(0.5f, OperationType.SET)); + // modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + // MvcResult mvcResult = mockMvc + // .perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + // // check impacts + // String resultAsString = mvcResult.getResponse().getContentAsString(); + // NetworkModificationResult networkModificationResult = mapper.readValue(resultAsString, + // NetworkModificationResult.class); + // assertEquals(1, networkModificationResult.getNetworkImpacts().size()); + // assertEquals(1, networkModificationResult.getImpactedSubstationsIds().size()); + // assertEquals("[s2]", networkModificationResult.getImpactedSubstationsIds().toString()); + // // modify only participate + // batteryModificationInfos.setParticipate(new AttributeModification<>(false, OperationType.SET)); + // modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + // mvcResult = mockMvc + // .perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + // // check impacts + // resultAsString = mvcResult.getResponse().getContentAsString(); + // networkModificationResult = mapper.readValue(resultAsString, NetworkModificationResult.class); + // assertEquals(1, networkModificationResult.getNetworkImpacts().size()); + // assertEquals(1, networkModificationResult.getImpactedSubstationsIds().size()); + // assertEquals("[s2]", networkModificationResult.getImpactedSubstationsIds().toString()); + + // } + + // @Test + // void testActivePowerZeroOrBetweenMinAndMaxActivePower() throws Exception { + // BatteryModificationInfos batteryModificationInfos = (BatteryModificationInfos) buildModification(); + // Battery battery = getNetwork().getBattery("v3Battery"); + // battery.setTargetP(80.) + // .setMinP(0.) + // .setMaxP(100.); + // batteryModificationInfos.setTargetP(new AttributeModification<>(155.0, OperationType.SET)); + + // Double minActivePower = batteryModificationInfos.getMinP() != null + // ? batteryModificationInfos.getMinP().getValue() + // : battery.getMinP(); + // Double maxActivePower = batteryModificationInfos.getMaxP() != null + // ? batteryModificationInfos.getMaxP().getValue() + // : battery.getMaxP(); + // Double activePower = batteryModificationInfos.getTargetP() != null + // ? batteryModificationInfos.getTargetP().getValue() + // : battery.getTargetP(); + + // String modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + // mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + // assertLogMessage("MODIFY_BATTERY_ERROR : Battery '" + "v3Battery" + "' : Active power " + activePower + // + " is expected to be equal to 0 or within the range of minimum active power and maximum active power: [" + // + minActivePower + ", " + maxActivePower + "]", + // batteryModificationInfos.getErrorType().name(), reportService); + + // } + + // @Test + // void testMinQGreaterThanMaxQ() throws Exception { + // BatteryModificationInfos batteryModificationInfos = (BatteryModificationInfos) buildModification(); + // Battery battery = getNetwork().getBattery("v3Battery"); + // battery.newReactiveCapabilityCurve() + // .beginPoint() + // .setP(0.) + // .setMaxQ(100.) + // .setMinQ(0.) + // .endPoint() + // .beginPoint() + // .setP(200.) + // .setMaxQ(150.) + // .setMinQ(0.) + // .endPoint() + // .add(); + // Collection points = battery + // .getReactiveLimits(ReactiveCapabilityCurve.class).getPoints(); + // List batteryPoints = new ArrayList<>(points); + // List modificationPoints = batteryModificationInfos + // .getReactiveCapabilityCurvePoints(); + // AtomicReference maxQ = new AtomicReference<>(Double.NaN); + // AtomicReference minQ = new AtomicReference<>(Double.NaN); + // if (!CollectionUtils.isEmpty(points)) { + // IntStream.range(0, modificationPoints.size()) + // .forEach(i -> { + // ReactiveCapabilityCurve.Point oldPoint = batteryPoints.get(i); + // ReactiveCapabilityCurveModificationInfos newPoint = modificationPoints + // .get(i); + // Double oldMaxQ = Double.NaN; + // Double oldMinQ = Double.NaN; + // if (oldPoint != null) { + // oldMaxQ = oldPoint.getMaxQ(); + // oldMinQ = oldPoint.getMinQ(); + // } + // newPoint.setMinQ(300.0); + // newPoint.setOldMaxQ(250.0); + // maxQ.set(newPoint.getMaxQ() != null ? newPoint.getMaxQ() : oldMaxQ); + // minQ.set(newPoint.getMinQ() != null ? newPoint.getMinQ() : oldMinQ); + // }); + // } + // String modificationToCreateJson = mapper.writeValueAsString(batteryModificationInfos); + // mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson) + // .contentType(MediaType.APPLICATION_JSON)) + // .andExpect(status().isOk()).andReturn(); + // assertLogMessage("MODIFY_BATTERY_ERROR : Battery '" + "v3Battery" + "' : maximum reactive power " + // + maxQ.get() + " is expected to be greater than or equal to minimum reactive power " + // + minQ.get(), + // batteryModificationInfos.getErrorType().name(), reportService); + // } + + // @Override + // protected void testCreationModificationMessage(ModificationInfos modificationInfos) throws Exception { + // assertEquals("BATTERY_MODIFICATION", modificationInfos.getMessageType()); + // Map updatedValues = mapper.readValue(modificationInfos.getMessageValues(), + // new TypeReference<>() { + // }); + // assertEquals("v3Battery", updatedValues.get("equipmentId")); + // } + + // @Override + // protected void testUpdateModificationMessage(ModificationInfos modificationInfos) throws Exception { + // assertEquals("BATTERY_MODIFICATION", modificationInfos.getMessageType()); + // Map updatedValues = mapper.readValue(modificationInfos.getMessageValues(), + // new TypeReference<>() { + // }); + // assertEquals("idBatteryEdited", updatedValues.get("equipmentId")); + // } + + // @Test + // void testDisconnection() throws Exception { + // assertChangeConnectionState(getNetwork().getBattery("v3Battery"), false); + // } + + // @Test + // void testConnection() throws Exception { + // assertChangeConnectionState(getNetwork().getBattery("v3Battery"), true); + // } +} diff --git a/src/test/java/org/gridsuite/modification/utils/NetworkCreation.java b/src/test/java/org/gridsuite/modification/utils/NetworkCreation.java new file mode 100644 index 0000000..4ece8c8 --- /dev/null +++ b/src/test/java/org/gridsuite/modification/utils/NetworkCreation.java @@ -0,0 +1,557 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.utils; + +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControlAdder; +import com.powsybl.network.store.iidm.impl.NetworkFactoryImpl; + +import java.util.UUID; + +import static org.gridsuite.modification.utils.NetworkUtil.*; + +public final class NetworkCreation { + public static final String VARIANT_ID = "variant_1"; + + private NetworkCreation() { + throw new IllegalCallerException("Utility class"); + } + + public static Network create(UUID uuid, boolean createHvdcLine) { + return create(uuid, createHvdcLine, new NetworkFactoryImpl()); + } + + //Create a network with fictitious switch and different switch kind + public static Network createSwitchNetwork(UUID uuid, NetworkFactory networkFactory) { + Network network = networkFactory.createNetwork(uuid.toString(), "test"); + + Substation s1 = network.newSubstation() + .setId("s1") + .setName("s1") + .add(); + VoltageLevel vl1 = createVoltageLevel(s1, "vl1", "vl1", TopologyKind.NODE_BREAKER, 400.0); + vl1.getNodeBreakerView().newBusbarSection() + .setId("b1") + .setName("b1") + .setNode(0) + .add(); + + VoltageLevel vl2 = createVoltageLevel(s1, "vl2", "vl2", TopologyKind.NODE_BREAKER, 400.0); + vl2.getNodeBreakerView().newBusbarSection() + .setId("b2") + .setName("b2") + .setNode(0) + .add(); + + createSwitch(vl1, "b4", "b4", SwitchKind.DISCONNECTOR, false, false, false, 0, 1); + createSwitch(vl1, "br11", "br11", SwitchKind.BREAKER, false, false, false, 1, 2); + createSwitch(vl2, "b5", "b5", SwitchKind.DISCONNECTOR, false, false, false, 0, 1); + createSwitch(vl2, "br21", "br21", SwitchKind.BREAKER, false, false, true, 1, 2); + + network.newLine() + .setId("line2") + .setName("line2") + .setVoltageLevel1("vl1") + .setVoltageLevel2("vl2") + .setR(0.1) + .setX(10.0) + .setG1(0.0) + .setG2(0.0) + .setB1(0.0) + .setB2(0.0) + .setNode1(2) + .setNode2(2) + .add(); + + network.getVariantManager().setWorkingVariant(VariantManagerConstants.INITIAL_VARIANT_ID); + return network; + } + + public static Network create(UUID uuid, boolean createHvdcLine, NetworkFactory networkFactory) { + Network network = networkFactory.createNetwork(uuid.toString(), "test"); + + Substation s1 = createSubstation(network, "s1", "s1", Country.FR); + VoltageLevel v1 = createVoltageLevel(s1, "v1", "v1", TopologyKind.NODE_BREAKER, 380.0); + createBusBarSection(v1, "1.1", "1.1", 0); + + createLoad(v1, "v1load", "v1load", 2, 0., 0., "cn1", 0, ConnectablePosition.Direction.BOTTOM); + createSwitch(v1, "v1d1", "v1d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 1); + createSwitch(v1, "v1b1", "v1b1", SwitchKind.BREAKER, true, false, false, 1, 2); + + createLccConverterStation(v1, "v1lcc", "v1lcc", 11, 0, 0); + createSwitch(v1, "v1dlcc", "v1dlcc", SwitchKind.DISCONNECTOR, true, false, false, 0, 12); + createSwitch(v1, "v1blcc", "v1blcc", SwitchKind.BREAKER, true, false, false, 12, 11); + + VoltageLevel v2 = createVoltageLevel(s1, "v2", "v2", TopologyKind.NODE_BREAKER, 225.0); + createBusBarSection(v2, "1A", "1A", 0); + createBusBarSection(v2, "1B", "1B", 1); + createSwitch(v2, "v2d1", "v2d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + createSwitch(v2, "v2b1", "v2b1", SwitchKind.BREAKER, true, true, false, 2, 3); + createSwitch(v2, "v2d2", "v2d2", SwitchKind.DISCONNECTOR, true, false, false, 3, 1); + + createLoad(v2, "v2load", "v2load", 5, 0., 0., "cn2", 2, ConnectablePosition.Direction.BOTTOM); + createSwitch(v2, "v2dload", "v2dload", SwitchKind.DISCONNECTOR, true, false, false, 1, 4); + createSwitch(v2, "v2bload", "v2bload", SwitchKind.BREAKER, true, false, false, 4, 5); + + createGenerator(v2, "idGenerator", 6, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v2, "v2bgenerator", "v2bgenerator", SwitchKind.BREAKER, true, false, false, 6, 7); + createSwitch(v2, "v2dgenerator", "v2dgenerator", SwitchKind.DISCONNECTOR, true, false, false, 7, 1); + + createShuntCompensator(v2, "v2shunt", "v2shunt", 8, 225., 10, true, 3, 1, 2, 2, "cn11", 22, ConnectablePosition.Direction.BOTTOM); + createSwitch(v2, "v2bshunt", "v2bshunt", SwitchKind.BREAKER, true, false, false, 8, 9); + createSwitch(v2, "v2dshunt", "v2dshunt", SwitchKind.DISCONNECTOR, true, false, false, 9, 0); + + createDanglingLine(v2, "v2Dangling", "v2Dangling", 10, 1, 2, 3, 4, 50, 30, "xnode1"); + createSwitch(v2, "v2bdangling", "v2bdangling", SwitchKind.BREAKER, true, false, false, 10, 11); + createSwitch(v2, "v2ddangling", "v2ddangling", SwitchKind.DISCONNECTOR, true, false, false, 11, 1); + + createVscConverterStation(v2, "v2vsc", "v2vsc", 12, 1, 40, true, 150); + createSwitch(v2, "v2bvsc", "v2bdvsc", SwitchKind.BREAKER, true, false, false, 12, 13); + createSwitch(v2, "v2dvsc", "v2dvsc", SwitchKind.DISCONNECTOR, true, false, false, 13, 0); + + VoltageLevel v4 = createVoltageLevel(s1, "v4", "v4", TopologyKind.NODE_BREAKER, 380.0); + createBusBarSection(v4, "1.A", "1.A", 0); + + Substation s3 = createSubstation(network, "s3", "s3", Country.FR); + s3.setProperty("tso", "rtefrance"); + s3.setProperty("region", "west"); + VoltageLevel v5 = createVoltageLevel(s3, "v5", "v5", TopologyKind.NODE_BREAKER, 380.0); + createBusBarSection(v5, "1A1", "1A1", 0); + createLoad(v5, "v5load", "v5load", 2, 0., 0., "cn5", 5, ConnectablePosition.Direction.TOP); + createShuntCompensator(v5, "v5shunt", "v5shunt", 4, 225., 10, true, 3, 1, 2, 2, "cn22", 33, ConnectablePosition.Direction.BOTTOM); + createGenerator(v5, "v5generator", 3, 42.1, 1.0, "cn10", 10, ConnectablePosition.Direction.TOP); + createStaticVarCompensator(v5, "v5Compensator", "v5Compensator", 5, StaticVarCompensator.RegulationMode.VOLTAGE, 380., 100, 2, 30); + + VoltageLevel v6 = createVoltageLevel(s3, "v6", "v6", TopologyKind.NODE_BREAKER, 380.0); + createBusBarSection(v6, "1B1", "1B1", 0); + createLoad(v6, "v6load", "v6load", 2, 0., 0., "cn6", 6, ConnectablePosition.Direction.BOTTOM); + createShuntCompensator(v6, "v6shunt", "v6shunt", 4, 225., 10, true, 3, 1, 2, 2, "cn33", 44, ConnectablePosition.Direction.BOTTOM); + createGenerator(v6, "v6generator", 3, 42.1, 1.0, "cn11", 11, ConnectablePosition.Direction.TOP); + createStaticVarCompensator(v6, "v6Compensator", "v6Compensator", 5, StaticVarCompensator.RegulationMode.VOLTAGE, 380., 100, 2, 30); + + Substation s2 = createSubstation(network, "s2", "s2", Country.FR); + VoltageLevel v3 = createVoltageLevel(s2, "v3", "v3", TopologyKind.NODE_BREAKER, 380.0, 15.0, 25.0); + createBusBarSection(v3, "3A", "3A", 0); + + createLoad(v3, "v3load", "v3load", 2, 0., 0., "cn3", 3, ConnectablePosition.Direction.BOTTOM); + createSwitch(v3, "v3d1", "v3d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 1); + createSwitch(v3, "v3b1", "v3b1", SwitchKind.BREAKER, true, false, false, 1, 2); + + createStaticVarCompensator(v3, "v3Compensator", "v3Compensator", 4, StaticVarCompensator.RegulationMode.VOLTAGE, 380., 100, 2, 30); + createSwitch(v3, "v3dCompensator", "v3dCompensator", SwitchKind.DISCONNECTOR, true, false, false, 0, 3); + createSwitch(v3, "v3bCompensator", "v3bCompensator", SwitchKind.BREAKER, true, false, false, 3, 4); + + createBattery(v1, "v1Battery", "v1Battery", 80, 0, 15, 6, 3); + createSwitch(v1, "v1dBattery", "v1dBattery", SwitchKind.DISCONNECTOR, true, false, false, 0, 81); + createSwitch(v1, "v1bBattery", "v1bBattery", SwitchKind.BREAKER, true, false, false, 81, 80); + + createBattery(v2, "v2Battery", "v2Battery", 50, 0, 20, 7, 11); + createSwitch(v2, "v2dBattery", "v2dBattery", SwitchKind.DISCONNECTOR, true, false, false, 0, 51); + createSwitch(v2, "v2bBattery", "v2bBattery", SwitchKind.BREAKER, true, false, false, 51, 50); + + createBattery(v3, "v3Battery", "v3Battery", 6, 0, 10, 1, 1); + createSwitch(v3, "v3dBattery", "v3dBattery", SwitchKind.DISCONNECTOR, true, false, false, 0, 5); + createSwitch(v3, "v3bBattery", "v3bBattery", SwitchKind.BREAKER, true, false, false, 5, 6); + + TwoWindingsTransformer t2 = createTwoWindingsTransformer(s1, "trf1", "trf1", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 4, 14, v1.getId(), v2.getId(), + "trf1", 1, ConnectablePosition.Direction.TOP, + "trf1", 2, ConnectablePosition.Direction.TOP); + t2.newRatioTapChanger() + .setLowTapPosition(0) + .setTapPosition(1) + .setLoadTapChangingCapabilities(false) + .setRegulating(true) + .setTargetDeadband(1.0) + .setTargetV(220.0) + .setRegulationTerminal(t2.getTerminal1()) + .beginStep() + .setR(39.78473) + .setX(39.784725) + .setG(0.0) + .setB(0.0) + .setRho(1.0) + .endStep() + .beginStep() + .setR(39.78474) + .setX(39.784726) + .setG(0.0) + .setB(0.0) + .setRho(1.0) + .endStep() + .beginStep() + .setR(39.78475) + .setX(39.784727) + .setG(0.0) + .setB(0.0) + .setRho(1.0) + .endStep() + .add(); + + createSwitch(v1, "v1btrf1", "v1btrf1", SwitchKind.BREAKER, true, false, false, 4, 5); + createSwitch(v1, "v1dtrf1", "v1dtrf1", SwitchKind.DISCONNECTOR, true, false, false, 5, 0); + createSwitch(v2, "v2btrf1", "v2btrf1", SwitchKind.BREAKER, true, false, false, 14, 15); + createSwitch(v2, "v2dtrf1", "v2dtrf1", SwitchKind.DISCONNECTOR, true, false, false, 15, 1); + + TwoWindingsTransformer twt2 = createTwoWindingsTransformer(s1, "trf2", "trf2", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 40, 150, v1.getId(), v2.getId(), + "trf2", 1, ConnectablePosition.Direction.TOP, + "trf2", 2, ConnectablePosition.Direction.TOP); + Terminal phaseTapChangerTerminal = ModificationUtils.getInstance().getTerminalFromIdentifiable(network, + "v3load", + "LOAD", + "V3"); + twt2.newPhaseTapChanger() + .setLowTapPosition(0) + .setTapPosition(1) + .setRegulationTerminal(phaseTapChangerTerminal) + .setRegulationMode(PhaseTapChanger.RegulationMode.FIXED_TAP) + .setTargetDeadband(2.) + .beginStep() + .setR(39.78473) + .setX(39.784725) + .setG(0.0) + .setB(0.0) + .setRho(1.0) + .setAlpha(1.) + .endStep() + .beginStep() + .setR(39.78475) + .setX(39.784727) + .setG(0.0) + .setB(0.0) + .setRho(1.0) + .setAlpha(1.1) + .endStep() + .add(); + + createSwitch(v1, "v1btrf2", "v1btrf2", SwitchKind.BREAKER, true, false, false, 40, 50); + createSwitch(v1, "v1dtrf2", "v1dtrf2", SwitchKind.DISCONNECTOR, true, false, false, 50, 0); + createSwitch(v2, "v2dtrf2", "v2dtrf2", SwitchKind.DISCONNECTOR, true, false, false, 150, 1); + + ThreeWindingsTransformer t3 = createThreeWindingsTransformer(s1, "trf6", "trf6", v1.getId(), v2.getId(), v4.getId(), + 0.5, 0.5, 0.5, 1., 1., 1., 0.1, 0.1, + 400., 225., 225., + 51, 16, 1, + "trf61", 5, ConnectablePosition.Direction.TOP, + "trf62", 5, ConnectablePosition.Direction.TOP, + "trf63", 3, ConnectablePosition.Direction.TOP); + createSwitch(v1, "v1btrf6", "v1btrf6", SwitchKind.BREAKER, true, false, false, 51, 6); + createSwitch(v1, "v1dtrf6", "v1dtrf6", SwitchKind.DISCONNECTOR, true, false, false, 6, 0); + createSwitch(v2, "v2btrf6", "v2btrf6", SwitchKind.BREAKER, true, false, false, 16, 17); + createSwitch(v2, "v2dtrf6", "v2dtrf6", SwitchKind.DISCONNECTOR, true, false, false, 17, 0); + createSwitch(v4, "v4btrf6", "v4btrf6", SwitchKind.BREAKER, true, false, false, 1, 2); + createSwitch(v4, "v4dtrf6", "v4dtrf6", SwitchKind.DISCONNECTOR, true, false, false, 2, 0); + + t3.getLeg1().newPhaseTapChanger() + .setTapPosition(1) + .setLowTapPosition(0) + .setRegulating(false) + .setTargetDeadband(1.0) + .setRegulationMode(PhaseTapChanger.RegulationMode.ACTIVE_POWER_CONTROL) + .setRegulationValue(10.0) + .setRegulationTerminal(t3.getLeg1().getTerminal()) + .beginStep() + .setR(1.0) + .setX(2.0) + .setG(3.0) + .setB(4.0) + .setAlpha(5.0) + .setRho(6.0) + .endStep() + .beginStep() + .setR(1.0) + .setX(2.0) + .setG(3.0) + .setB(4.0) + .setAlpha(5.0) + .setRho(6.0) + .endStep() + .add(); + + // create lines + createLine(network, "line1", "line1", "v3", "v4", 8, 4, 1.0, 1.0, 1.0, 2.0, 1.0, 2.0, "cn1line1", 1, ConnectablePosition.Direction.TOP, "cn2line1", 1, ConnectablePosition.Direction.TOP); + createSwitch(v3, "v3dl1", "v3dl1", SwitchKind.DISCONNECTOR, true, false, false, 0, 7); + createSwitch(v3, "v3bl1", "v3bl1", SwitchKind.BREAKER, true, false, false, 7, 8); + createSwitch(v4, "v4dl1", "v4dl1", SwitchKind.DISCONNECTOR, true, false, false, 0, 3); + createSwitch(v4, "v4bl1", "v4bl1", SwitchKind.BREAKER, true, false, false, 3, 4); + + createLine(network, "line2", "line2", "v1", "v3", 31, 31, 10.0, 5.0, 3.5, 5.5, 4.5, 6.5, "cn1line2", 2, ConnectablePosition.Direction.TOP, "cn2line2", 2, ConnectablePosition.Direction.TOP); + createSwitch(v1, "v1dl2", "v1dl2", SwitchKind.DISCONNECTOR, true, false, false, 0, 30); + createSwitch(v1, "v1bl2", "v1bl2", SwitchKind.BREAKER, true, false, false, 30, 31); + createSwitch(v3, "v3dl2", "v3dl2", SwitchKind.DISCONNECTOR, true, false, false, 0, 30); + createSwitch(v3, "v3bl2", "v3bl2", SwitchKind.BREAKER, true, false, false, 30, 31); + + createLineWithoutConnectivity(network, "line3", "line3", "v1", "v3", 10, 12, 12.0, 7.0, 5.5, 7.5, 6.5, 8.5); + createSwitch(v1, "v1dl3", "v1dl3", SwitchKind.DISCONNECTOR, true, false, false, 0, 9); + createSwitch(v1, "v1bl3", "v1bl3", SwitchKind.BREAKER, true, false, true, 9, 10); + createSwitch(v3, "v3dl3", "v3dl3", SwitchKind.DISCONNECTOR, true, false, false, 0, 11); + createSwitch(v3, "v3bl3", "v3bl3", SwitchKind.BREAKER, true, false, true, 11, 12); + + if (createHvdcLine) { + createHvdcLine(network, "hvdcLine", "hvdcLine", 1, 100, HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER, 225, 500, "v1lcc", "v2vsc"); + } + + // Creating new variant with new equipments + network.getVariantManager().cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, VARIANT_ID); + network.getVariantManager().setWorkingVariant(VARIANT_ID); + + Substation s1Variant = createSubstation(network, "s1Variant", "s1Variant", Country.FR); + VoltageLevel v1Variant = createVoltageLevel(s1Variant, "v1Variant", "v1Variant", TopologyKind.NODE_BREAKER, 380.0); + createBusBarSection(v1Variant, "bbs1Variant", "bbs1Variant", 0); + createSwitch(v1Variant, "disc1Variant", "disc1Variant", SwitchKind.DISCONNECTOR, true, true, false, 0, 1); + createSwitch(v1Variant, "break1Variant", "break1Variant", SwitchKind.BREAKER, true, false, false, 1, 2); + createLoad(v1Variant, "load1Variant", "load1Variant", 2, 0., 0., "cn1", 0, ConnectablePosition.Direction.BOTTOM); + + Substation s2Variant = createSubstation(network, "s2Variant", "s2Variant", Country.FR); + VoltageLevel v2Variant = createVoltageLevel(s2Variant, "v2Variant", "v2Variant", TopologyKind.NODE_BREAKER, 380.0); + createBusBarSection(v2Variant, "bbs2Variant", "bbs2Variant", 0); + createSwitch(v2Variant, "disc2Variant", "disc2Variant", SwitchKind.DISCONNECTOR, true, true, false, 0, 1); + createSwitch(v2Variant, "break2Variant", "break2Variant", SwitchKind.BREAKER, true, false, false, 1, 2); + createLoad(v2Variant, "load2Variant", "load2Variant", 2, 0., 0., "cn1", 0, ConnectablePosition.Direction.BOTTOM); + + createSwitch(v1Variant, "disc11Variant", "disc11Variant", SwitchKind.DISCONNECTOR, true, false, false, 0, 3); + createSwitch(v1Variant, "break11Variant", "break11Variant", SwitchKind.BREAKER, true, false, false, 3, 4); + createSwitch(v2Variant, "dsc21Variant", "disc21Variant", SwitchKind.DISCONNECTOR, true, false, false, 0, 3); + createSwitch(v2Variant, "break21Variant", "break21Variant", SwitchKind.BREAKER, true, false, false, 3, 4); + + createLine(network, "line1Variant", "line1Variant", "v1Variant", "v2Variant", 4, 4, 10.0, 5.0, 3.5, 5.5, 4.5, 6.5, "cn1line1Variant", 1, ConnectablePosition.Direction.TOP, "cn2line1Variant", 1, ConnectablePosition.Direction.TOP); + + network.getVariantManager().setWorkingVariant(VariantManagerConstants.INITIAL_VARIANT_ID); + + return network; + } + + public static Network createGeneratorsNetwork(UUID uuid, NetworkFactory networkFactory) { + Network network = networkFactory.createNetwork(uuid.toString(), "test"); + + Substation s1 = createSubstation(network, "s1", "s1", Country.FR); + VoltageLevel v1 = createVoltageLevel(s1, "v1", "v1", TopologyKind.NODE_BREAKER, 380.0); + createBusBarSection(v1, "1.1", "1.1", 0); + createBusBarSection(v1, "1.2", "1.2", 1); + createGenerator(v1, "gen1", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v1, "v1d1", "v1d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + + VoltageLevel v2 = createVoltageLevel(s1, "v2", "v2", TopologyKind.NODE_BREAKER, 225.0); + createBusBarSection(v2, "2.1", "2.1", 0); + createBusBarSection(v2, "2.2", "2.2", 1); + createGenerator(v2, "gen2", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createGenerator(v2, "gen3", 3, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v2, "v2d1", "v2d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + createSwitch(v2, "v2d2", "v2d2", SwitchKind.DISCONNECTOR, true, false, false, 0, 3); + + Substation s2 = createSubstation(network, "s2", "s2", Country.FR); + VoltageLevel v21 = createVoltageLevel(s2, "v21", "v21", TopologyKind.NODE_BREAKER, 450.0); + createBusBarSection(v21, "3.1", "3.1", 0); + createBusBarSection(v21, "3.2", "3.2", 1); + createGenerator(v21, "gen4", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v21, "v21d1", "v21d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + + VoltageLevel v22 = createVoltageLevel(s2, "v22", "v22", TopologyKind.NODE_BREAKER, 225.0); + createBusBarSection(v22, "4.1", "4.1", 0); + createBusBarSection(v22, "4.2", "4.2", 1); + createGenerator(v22, "gen5", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createGenerator(v22, "gen6", 3, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v22, "v22d1", "v22d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + createSwitch(v22, "v22d2", "v22d2", SwitchKind.DISCONNECTOR, true, false, false, 0, 3); + + Substation s3 = createSubstation(network, "s3", "s3", Country.FR); + VoltageLevel v31 = createVoltageLevel(s3, "v31", "v31", TopologyKind.NODE_BREAKER, 450.0); + createBusBarSection(v31, "5.1", "5.1", 0); + createBusBarSection(v31, "5.2", "5.2", 1); + createGenerator(v31, "gen7", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createGenerator(v31, "gen8", 3, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v31, "v31d1", "v31d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + createSwitch(v31, "v31d2", "v31d2", SwitchKind.DISCONNECTOR, true, false, false, 1, 3); + + VoltageLevel v32 = createVoltageLevel(s3, "v32", "v32", TopologyKind.NODE_BREAKER, 225.0); + createBusBarSection(v32, "6.1", "6.1", 0); + createBusBarSection(v32, "6.2", "6.2", 1); + createGenerator(v32, "gen9", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createGenerator(v32, "gen10", 3, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v32, "v32d1", "v32d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + createSwitch(v32, "v32d2", "v32d2", SwitchKind.DISCONNECTOR, true, false, false, 1, 3); + + network.getVariantManager().setWorkingVariant(VariantManagerConstants.INITIAL_VARIANT_ID); + network.getVariantManager().cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, VARIANT_ID); + + return network; + } + + public static Network createLoadNetwork(UUID uuid, NetworkFactory networkFactory) { + Network network = networkFactory.createNetwork(uuid.toString(), "test"); + + Substation s1 = createSubstation(network, "s1", "s1", Country.FR); + VoltageLevel v1 = createVoltageLevel(s1, "v1", "v1", TopologyKind.NODE_BREAKER, 380.0); + createBusBarSection(v1, "1.1", "1.1", 0); + createBusBarSection(v1, "1.2", "1.2", 1); + createLoad(v1, "load1", "load1", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v1, "v1d1", "v1d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + + VoltageLevel v2 = createVoltageLevel(s1, "v2", "v2", TopologyKind.NODE_BREAKER, 225.0); + createBusBarSection(v2, "2.1", "2.1", 0); + createBusBarSection(v2, "2.2", "2.2", 1); + createLoad(v2, "load2", "load2", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createLoad(v2, "load3", "load3", 3, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v2, "v2d1", "v2d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + createSwitch(v2, "v2d2", "v2d2", SwitchKind.DISCONNECTOR, true, false, false, 0, 3); + + Substation s2 = createSubstation(network, "s2", "s2", Country.FR); + VoltageLevel v21 = createVoltageLevel(s2, "v21", "v21", TopologyKind.NODE_BREAKER, 450.0); + createBusBarSection(v21, "3.1", "3.1", 0); + createBusBarSection(v21, "3.2", "3.2", 1); + createLoad(v21, "load4", "load4", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v21, "v21d1", "v21d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + + VoltageLevel v22 = createVoltageLevel(s2, "v22", "v22", TopologyKind.NODE_BREAKER, 225.0); + createBusBarSection(v22, "4.1", "4.1", 0); + createBusBarSection(v22, "4.2", "4.2", 1); + createLoad(v22, "load5", "load5", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createLoad(v22, "load6", "load6", 3, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v22, "v22d1", "v22d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + createSwitch(v22, "v22d2", "v22d2", SwitchKind.DISCONNECTOR, true, false, false, 0, 3); + + Substation s3 = createSubstation(network, "s3", "s3", Country.FR); + VoltageLevel v31 = createVoltageLevel(s3, "v31", "v31", TopologyKind.NODE_BREAKER, 450.0); + createBusBarSection(v31, "5.1", "5.1", 0); + createBusBarSection(v31, "5.2", "5.2", 1); + createLoad(v31, "load7", "load7", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createLoad(v31, "load8", "load8", 3, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v31, "v31d1", "v31d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + createSwitch(v31, "v31d2", "v31d2", SwitchKind.DISCONNECTOR, true, false, false, 1, 3); + + VoltageLevel v32 = createVoltageLevel(s3, "v32", "v32", TopologyKind.NODE_BREAKER, 225.0); + createBusBarSection(v32, "6.1", "6.1", 0); + createBusBarSection(v32, "6.2", "6.2", 1); + createLoad(v32, "load9", "load9", 2, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createLoad(v32, "load10", "load10", 3, 42.1, 1.0, "cn0", 3, ConnectablePosition.Direction.TOP); + createSwitch(v32, "v32d1", "v32d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 2); + createSwitch(v32, "v32d2", "v32d2", SwitchKind.DISCONNECTOR, true, false, false, 1, 3); + + network.getVariantManager().setWorkingVariant(VariantManagerConstants.INITIAL_VARIANT_ID); + network.getVariantManager().cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, VARIANT_ID); + + return network; + } + + public static Network createBusBreaker(UUID uuid) { + Network network = new NetworkFactoryImpl().createNetwork(uuid.toString(), "test"); + + Substation s1 = createSubstation(network, "s1", "s1", Country.FR); + VoltageLevel v1 = createVoltageLevel(s1, "v1", "v1", TopologyKind.BUS_BREAKER, 380.0); + VoltageLevel v12 = createVoltageLevel(s1, "v12", "v12", TopologyKind.BUS_BREAKER, 90.0); + createBus(v1, "bus1", "bus1"); + createGeneratorOnBus(v1, "idGenerator1", "bus1", 42.1, 1.0); + createBus(v12, "bus12", "bus12"); + Substation s2 = createSubstation(network, "s2", "s2", Country.FR); + VoltageLevel v2 = createVoltageLevel(s2, "v2", "v2", TopologyKind.BUS_BREAKER, 225.0); + createBus(v2, "bus2", "bus2"); + return network; + } + + public static Network createMixedTopology(UUID uuid) { + Network network = new NetworkFactoryImpl().createNetwork(uuid.toString(), "test"); + + Substation s1 = createSubstation(network, "s1", "s1", Country.FR); + VoltageLevel v1 = createVoltageLevel(s1, "v1", "v1", TopologyKind.NODE_BREAKER, 380.0); + createBusBarSection(v1, "1.1", "1.1", 0); + createSwitch(v1, "v1d1", "v1d1", SwitchKind.DISCONNECTOR, true, false, false, 0, 1); + createSwitch(v1, "v1b1", "v1b1", SwitchKind.BREAKER, true, false, false, 1, 2); + createLoad(v1, "v1load", "v1load", 2, 0., 0., "cn1", 0, ConnectablePosition.Direction.BOTTOM); + createLccConverterStation(v1, "v1lcc", "v1lcc", 3, 0, 0); + VoltageLevel v3 = createVoltageLevel(s1, "v3", "v3", TopologyKind.BUS_BREAKER, 450.0); + createBus(v3, "bus3", "bus3"); + Substation s2 = createSubstation(network, "s2", "s2", Country.FR); + VoltageLevel v2 = createVoltageLevel(s2, "v2", "v2", TopologyKind.BUS_BREAKER, 225.0); + createBus(v2, "bus2", "bus2"); + + return network; + } + + /** + * Create a network as following: + *
+     *     VL1            VL2             VL3
+     *
+     *     ld1            g2              ld3
+     *      |              |               |
+     *     br1            br2             br3
+     *      |              |               |
+     *     d1             d2              d3
+     *      |              |               |
+     *     bbs1 ----------bbs2------------bbs3
+     *              l1             l2
+     * 
+ */ + public static Network createForDeleteVoltageLevelOnLine(UUID uuid) { + Network network = new NetworkFactoryImpl().createNetwork(uuid.toString(), "NetworkForDeleteVoltageLevelOnLine"); + + // VL1 + Substation s1 = createSubstation(network, "s1", null, Country.FR); + VoltageLevel v1 = createVoltageLevel(s1, "v1", null, TopologyKind.NODE_BREAKER, 380); + createBusBarSection(v1, "bbs1", null, 0); + createLoad(v1, "ld1", null, 2, 0., 0., "ld1", 0, ConnectablePosition.Direction.BOTTOM); + createSwitch(v1, "d1", null, SwitchKind.DISCONNECTOR, true, false, false, 0, 1); + createSwitch(v1, "br1", null, SwitchKind.BREAKER, true, false, false, 1, 2); + + // VL2 + Substation s2 = createSubstation(network, "s2", null, Country.FR); + VoltageLevel v2 = createVoltageLevel(s2, "v2", null, TopologyKind.NODE_BREAKER, 380); + createBusBarSection(v2, "bbs2", null, 0); + + createGenerator(v2, "g2", 2, 42.1, 1.0, "g2", 3, ConnectablePosition.Direction.TOP); + createSwitch(v2, "d2", null, SwitchKind.DISCONNECTOR, true, false, false, 0, 1); + createSwitch(v2, "br2", null, SwitchKind.BREAKER, true, false, false, 1, 2); + + // VL3 + Substation s3 = createSubstation(network, "s3", null, Country.FR); + VoltageLevel v3 = createVoltageLevel(s3, "v3", null, TopologyKind.NODE_BREAKER, 380.0); + createBusBarSection(v3, "bbs3", null, 0); + + createLoad(v3, "ld3", null, 2, 0., 0., "ld3", 3, ConnectablePosition.Direction.BOTTOM); + createSwitch(v3, "d3", null, SwitchKind.DISCONNECTOR, true, false, false, 0, 1); + createSwitch(v3, "br3", null, SwitchKind.BREAKER, true, false, false, 1, 2); + + // create lines + createLine(network, "l1", null, "v1", "v2", 4, 4, 1.0, 1.0, 1.0, 2.0, 1.0, 2.0, "l1", 1, ConnectablePosition.Direction.TOP, "l1", 1, ConnectablePosition.Direction.TOP); + createSwitch(v1, "l1d1", null, SwitchKind.DISCONNECTOR, true, false, false, 0, 5); + createSwitch(v1, "l1br1", null, SwitchKind.BREAKER, true, false, false, 5, 4); + createSwitch(v2, "l1d2", null, SwitchKind.DISCONNECTOR, true, false, false, 0, 5); + createSwitch(v2, "l1br2", null, SwitchKind.BREAKER, true, false, false, 5, 4); + + createLine(network, "l2", null, "v1", "v3", 6, 4, 10.0, 5.0, 3.5, 5.5, 4.5, 6.5, "l2", 2, ConnectablePosition.Direction.TOP, "l2", 2, ConnectablePosition.Direction.TOP); + createSwitch(v1, "l2d1", null, SwitchKind.DISCONNECTOR, true, false, false, 0, 7); + createSwitch(v1, "l2br1", null, SwitchKind.BREAKER, true, false, false, 7, 6); + createSwitch(v3, "l2d2", null, SwitchKind.DISCONNECTOR, true, false, false, 0, 5); + createSwitch(v3, "l2br2", null, SwitchKind.BREAKER, true, false, false, 5, 4); + + return network; + } + + public static Network createWithVSC(UUID uuid, boolean withAngleDropExtention) { + NetworkFactoryImpl networkFactory = new NetworkFactoryImpl(); + Network network = create(uuid, false, networkFactory); + VoltageLevel v1 = network.getVoltageLevel("v1"); + + createVscConverterStation(v1, "v1vsc", "v1vsc", 18, 1, 40, true, 150); + createSwitch(v1, "v1dvsc", "v1dvsc", SwitchKind.DISCONNECTOR, true, false, false, 0, 12); + createSwitch(v1, "v1bvsc", "v1bvsc", SwitchKind.BREAKER, true, false, false, 12, 18); + + createHvdcLine(network, "hvdcLine", "hvdcLine", 1, 100, HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER, 225, 500, "v1vsc", "v2vsc"); + HvdcLine hvdcLine = network.getHvdcLine("hvdcLine"); + if (withAngleDropExtention) { + hvdcLine.newExtension(HvdcAngleDroopActivePowerControlAdder.class) + .withEnabled(true) + .withP0(0F) + .withDroop(10F) + .add(); + } + + return network; + } +} diff --git a/src/test/java/org/gridsuite/modification/utils/NetworkUtil.java b/src/test/java/org/gridsuite/modification/utils/NetworkUtil.java new file mode 100644 index 0000000..72a28da --- /dev/null +++ b/src/test/java/org/gridsuite/modification/utils/NetworkUtil.java @@ -0,0 +1,388 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.modification.utils; + +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.BusbarSectionPositionAdder; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import com.powsybl.iidm.network.extensions.ConnectablePositionAdder; +import com.powsybl.iidm.network.extensions.IdentifiableShortCircuitAdder; + +public final class NetworkUtil { + private NetworkUtil() { + throw new IllegalStateException("Utility class"); + } + + @SuppressWarnings("SameParameterValue") + public static Substation createSubstation(Network n, String id, String name, Country country) { + return n.newSubstation() + .setId(id) + .setName(name) + .setCountry(country) + .add(); + } + + @SuppressWarnings("SameParameterValue") + public static VoltageLevel createVoltageLevel(Substation s, String id, String name, + TopologyKind topology, double vNom) { + return s.newVoltageLevel() + .setId(id) + .setName(name) + .setTopologyKind(topology) + .setNominalV(vNom) + .add(); + } + + public static VoltageLevel createVoltageLevel(Substation s, String id, String name, + TopologyKind topology, double vNom, double ipMin, double ipMax) { + VoltageLevel vl = createVoltageLevel(s, id, name, topology, vNom); + vl.newExtension(IdentifiableShortCircuitAdder.class).withIpMin(ipMin).withIpMax(ipMax).add(); + return vl; + } + + public static void createBusBarSection(VoltageLevel vl, String id, String name, int node) { + var bbs = vl.getNodeBreakerView().newBusbarSection() + .setId(id) + .setName(name) + .setNode(node) + .add(); + bbs.newExtension(BusbarSectionPositionAdder.class).withBusbarIndex(0).withSectionIndex(0).add(); + } + + public static void createBus(VoltageLevel vl, String id, String name) { + vl.getBusBreakerView().newBus() + .setId(id) + .setName(name) + .add(); + } + + @SuppressWarnings("SameParameterValue") + public static void createSwitch(VoltageLevel vl, String id, String name, SwitchKind kind, boolean retained, boolean open, boolean fictitious, int node1, int node2) { + vl.getNodeBreakerView().newSwitch() + .setId(id) + .setName(name) + .setKind(kind) + .setRetained(retained) + .setOpen(open) + .setFictitious(fictitious) + .setNode1(node1) + .setNode2(node2) + .add(); + } + + public static void createLine(Network network, String id, String name, String voltageLevel1, String voltageLevel2, int node1, int node2, + double r, double x, double g1, double g2, double b1, double b2, + String feederName1, int feederOrder1, ConnectablePosition.Direction direction1, + String feederName2, int feederOrder2, ConnectablePosition.Direction direction2) { + var l = network.newLine() + .setId(id) + .setName(name) + .setR(r) + .setX(x) + .setG1(g1) + .setG2(g2) + .setB1(b1) + .setB2(b2) + .setVoltageLevel1(voltageLevel1) + .setVoltageLevel2(voltageLevel2) + .setNode1(node1) + .setNode2(node2) + .add(); + + l.newExtension(ConnectablePositionAdder.class) + .newFeeder1() + .withName(feederName1) + .withOrder(feederOrder1) + .withDirection(direction1).add() + .newFeeder2() + .withName(feederName2) + .withOrder(feederOrder2) + .withDirection(direction2).add() + .add(); + } + + public static void createLineWithoutConnectivity(Network network, String id, String name, String voltageLevel1, String voltageLevel2, int node1, int node2, + double r, double x, double g1, double g2, double b1, double b2) { + network.newLine() + .setId(id) + .setName(name) + .setR(r) + .setX(x) + .setG1(g1) + .setG2(g2) + .setB1(b1) + .setB2(b2) + .setVoltageLevel1(voltageLevel1) + .setVoltageLevel2(voltageLevel2) + .setNode1(node1) + .setNode2(node2) + .add(); + } + + @SuppressWarnings("SameParameterValue") + public static void createLoad(VoltageLevel vl, String id, String name, + int node, double p0, double q0, String feederName, int feederOrder, ConnectablePosition.Direction direction) { + var l = vl.newLoad() + .setId(id) + .setName(name) + .setNode(node) + .setP0(p0) + .setQ0(q0) + .add(); + l.newExtension(ConnectablePositionAdder.class) + .newFeeder() + .withName(feederName) + .withOrder(feederOrder) + .withDirection(direction) + .add(); + } + + public static void createLccConverterStation(VoltageLevel vl, String id, String name, + int node, float powerFactor, float lossFactor) { + vl.newLccConverterStation() + .setId(id) + .setName(name) + .setNode(node) + .setLossFactor(lossFactor) + .setPowerFactor(powerFactor) + .add(); + } + + public static void createGenerator(VoltageLevel vl, String id, int node, double targetP, double targetQ, String feederName, int feederOrder, ConnectablePosition.Direction direction) { + createGenerator(vl, id, node, targetP, targetQ, feederName, feederOrder, direction, 1000.0, -1.1); + } + + @SuppressWarnings("SameParameterValue") + public static void createGenerator(VoltageLevel vl, String id, int node, double targetP, double targetQ, String feederName, int feederOrder, ConnectablePosition.Direction direction, double maxP, double minP) { + var g = vl.newGenerator() + .setId(id) + .setName(id) + .setTargetP(targetP) + .setTargetQ(targetQ) + .setNode(node) + .setMinP(minP) + .setMaxP(maxP) + .setVoltageRegulatorOn(false) + .add(); + + g.newExtension(ConnectablePositionAdder.class) + .newFeeder() + .withName(feederName) + .withOrder(feederOrder) + .withDirection(direction).add(); + } + + public static void createGeneratorOnBus(VoltageLevel vl, String id, String busId, double targetP, double targetQ) { + vl.newGenerator() + .setId(id) + .setName(id) + .setTargetP(targetP) + .setTargetQ(targetQ) + .setBus(busId) + .setConnectableBus(busId) + .setMinP(-1.1) + .setMaxP(1000.0) + .setVoltageRegulatorOn(false) + .add(); + } + + @SuppressWarnings("SameParameterValue") + public static TwoWindingsTransformer createTwoWindingsTransformer(Substation s, String id, String name, + double r, double x, double g, double b, + double ratedU1, double ratedU2, + int node1, int node2, + String idVoltageLevel1, String idVoltageLevel2, + String feederName1, int feederOrder1, ConnectablePosition.Direction direction1, + String feederName2, int feederOrder2, ConnectablePosition.Direction direction2) { + + TwoWindingsTransformer t = s.newTwoWindingsTransformer() + .setId(id) + .setName(name) + .setR(r) + .setX(x) + .setG(g) + .setB(b) + .setRatedU1(ratedU1) + .setRatedU2(ratedU2) + .setNode1(node1) + .setVoltageLevel1(idVoltageLevel1) + .setNode2(node2) + .setVoltageLevel2(idVoltageLevel2) + .add(); + + t.newExtension(ConnectablePositionAdder.class) + .newFeeder1() + .withName(feederName1) + .withOrder(feederOrder1) + .withDirection(direction1).add() + .newFeeder2() + .withName(feederName2) + .withOrder(feederOrder2) + .withDirection(direction2).add() + .add(); + + return t; + } + + @SuppressWarnings("SameParameterValue") + public static ThreeWindingsTransformer createThreeWindingsTransformer(Substation s, String id, String name, + String vl1, String vl2, String vl3, + double r1, double r2, double r3, + double x1, double x2, double x3, + double g1, double b1, + double ratedU1, double ratedU2, double ratedU3, + int node1, int node2, int node3, + String feederName1, int feederOrder1, ConnectablePosition.Direction direction1, + String feederName2, int feederOrder2, ConnectablePosition.Direction direction2, + String feederName3, int feederOrder3, ConnectablePosition.Direction direction3) { + ThreeWindingsTransformer t = s.newThreeWindingsTransformer() + .setId(id) + .setName(name) + .newLeg1() + .setR(r1) + .setX(x1) + .setG(g1) + .setB(b1) + .setRatedU(ratedU1) + .setVoltageLevel(vl1) + .setNode(node1) + .add() + .newLeg2() + .setR(r2) + .setX(x2) + .setRatedU(ratedU2) + .setVoltageLevel(vl2) + .setNode(node2) + .add() + .newLeg3() + .setR(r3) + .setX(x3) + .setRatedU(ratedU3) + .setVoltageLevel(vl3) + .setNode(node3) + .add() + .add(); + + t.newExtension(ConnectablePositionAdder.class) + .newFeeder1() + .withName(feederName1) + .withOrder(feederOrder1) + .withDirection(direction1).add() + .newFeeder2() + .withName(feederName2) + .withOrder(feederOrder2) + .withDirection(direction2).add() + .newFeeder3() + .withName(feederName3) + .withOrder(feederOrder3) + .withDirection(direction3).add() + .add(); + + return t; + } + + public static void createShuntCompensator(VoltageLevel vl, String id, String name, + int node, double targetV, double targetDeadband, boolean voltageRegulatorOn, + int maximumSectionCount, double bPerSection, double gPerSection, int sectionCount, String feederName, int feederOrder, ConnectablePosition.Direction direction) { + var sh = vl.newShuntCompensator() + .setId(id) + .setName(name) + .setNode(node) + .setTargetV(targetV) + .setTargetDeadband(targetDeadband) + .setVoltageRegulatorOn(voltageRegulatorOn) + .newLinearModel() + .setMaximumSectionCount(maximumSectionCount) + .setBPerSection(bPerSection) + .setGPerSection(gPerSection) + .add() + .setSectionCount(sectionCount) + .add(); + sh.newExtension(ConnectablePositionAdder.class) + .newFeeder() + .withName(feederName) + .withOrder(feederOrder) + .withDirection(direction).add(); + } + + public static void createStaticVarCompensator(VoltageLevel vl, String id, String name, + int node, StaticVarCompensator.RegulationMode regulationMode, double voltageSetpoint, + double reactivePowerSetpoint, double bMin, double bMax) { + vl.newStaticVarCompensator() + .setId(id) + .setName(name) + .setRegulationMode(regulationMode) + .setVoltageSetpoint(voltageSetpoint) + .setReactivePowerSetpoint(reactivePowerSetpoint) + .setBmin(bMin) + .setBmax(bMax) + .setNode(node) + .add(); + } + + public static void createBattery(VoltageLevel vl, String id, String name, + int node, double minP, double maxP, double targetP, double targetQ) { + vl.newBattery() + .setId(id) + .setName(name) + .setMinP(minP) + .setMaxP(maxP) + .setTargetP(targetP) + .setTargetQ(targetQ) + .setNode(node) + .add(); + } + + public static void createDanglingLine(VoltageLevel vl, String id, String name, + int node, double r, double x, double b, double g, double p0, double q0, String pairingKey) { + vl.newDanglingLine() + .setId(id) + .setName(name) + .setR(r) + .setX(x) + .setB(b) + .setG(g) + .setP0(p0) + .setQ0(q0) + .setPairingKey(pairingKey) + .setNode(node) + .add(); + } + + public static void createVscConverterStation(VoltageLevel vl, String id, String name, + int node, float lossFactor, + double reactivePowerSetpoint, boolean voltageRegulatorOn, double voltageSetpoint) { + VscConverterStation vsc = vl.newVscConverterStation() + .setId(id) + .setName(name) + .setNode(node) + .setLossFactor(lossFactor) + .setReactivePowerSetpoint(reactivePowerSetpoint) + .setVoltageRegulatorOn(voltageRegulatorOn) + .setVoltageSetpoint(voltageSetpoint) + .add(); + + vsc.newMinMaxReactiveLimits().setMinQ(0).setMaxQ(10).add(); + } + + public static void createHvdcLine(Network network, String id, String name, + double r, double maxP, HvdcLine.ConvertersMode hvdcLineConvertersMode, + double nominalV, double activePowerSetpoint, + String converterStationId1, String converterStationId2) { + network.newHvdcLine() + .setId(id) + .setName(name) + .setR(r) + .setMaxP(maxP) + .setConvertersMode(hvdcLineConvertersMode) + .setNominalV(nominalV) + .setActivePowerSetpoint(activePowerSetpoint) + .setConverterStationId1(converterStationId1) + .setConverterStationId2(converterStationId2) + .add(); + } +}