diff --git a/pom.xml b/pom.xml index 4ae6efa1a..7359947bc 100644 --- a/pom.xml +++ b/pom.xml @@ -144,6 +144,11 @@ com.powsybl powsybl-network-store-client + + com.powsybl + powsybl-network-store-iidm-impl + 1.7.0 + com.powsybl powsybl-ws-commons diff --git a/src/main/java/org/gridsuite/modification/server/dto/SubstationInfos.java b/src/main/java/org/gridsuite/modification/server/dto/SubstationInfos.java index cb1ae27d5..83ac6de37 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/SubstationInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/SubstationInfos.java @@ -6,10 +6,7 @@ */ package org.gridsuite.modification.server.dto; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import lombok.experimental.SuperBuilder; /** @@ -17,6 +14,7 @@ */ @SuperBuilder @NoArgsConstructor +@AllArgsConstructor @Getter @ToString @EqualsAndHashCode diff --git a/src/main/java/org/gridsuite/modification/server/dto/VoltageLevelInfos.java b/src/main/java/org/gridsuite/modification/server/dto/VoltageLevelInfos.java index 272eb9627..a4d244eb8 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/VoltageLevelInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/VoltageLevelInfos.java @@ -6,10 +6,7 @@ */ package org.gridsuite.modification.server.dto; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import lombok.experimental.SuperBuilder; /** @@ -17,6 +14,7 @@ */ @SuperBuilder @NoArgsConstructor +@AllArgsConstructor @Getter @ToString @EqualsAndHashCode diff --git a/src/main/java/org/gridsuite/modification/server/dto/elasticsearch/EquipmentInfos.java b/src/main/java/org/gridsuite/modification/server/dto/elasticsearch/EquipmentInfos.java index 9770e4e69..92b748e6d 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/elasticsearch/EquipmentInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/elasticsearch/EquipmentInfos.java @@ -101,5 +101,4 @@ public static Set getSubstationsInfos(@NonNull Identifiable .name(vl.getSubstation().map(Substation::getNameOrId).orElse(null)).build()) .collect(Collectors.toSet()); } - } diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/ReferenceFieldOrValue.java b/src/main/java/org/gridsuite/modification/server/dto/formula/ReferenceFieldOrValue.java index a7d2ea60e..aaae6b502 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/formula/ReferenceFieldOrValue.java +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/ReferenceFieldOrValue.java @@ -13,6 +13,7 @@ import com.powsybl.iidm.network.IdentifiableType; import com.powsybl.iidm.network.Load; import com.powsybl.iidm.network.ShuntCompensator; +import com.powsybl.iidm.network.TwoWindingsTransformer; import com.powsybl.iidm.network.VoltageLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -24,6 +25,7 @@ import org.gridsuite.modification.server.dto.formula.equipmentfield.GeneratorField; import org.gridsuite.modification.server.dto.formula.equipmentfield.LoadField; import org.gridsuite.modification.server.dto.formula.equipmentfield.ShuntCompensatorField; +import org.gridsuite.modification.server.dto.formula.equipmentfield.TwoWindingsTransformerField; import org.gridsuite.modification.server.dto.formula.equipmentfield.VoltageLevelField; @@ -52,21 +54,15 @@ public Double getRefOrValue(Identifiable identifiable) { } IdentifiableType identifiableType = identifiable.getType(); - Double referenceValue = switch (identifiableType) { + return switch (identifiableType) { case GENERATOR -> GeneratorField.getReferenceValue((Generator) identifiable, equipmentField); case BATTERY -> BatteryField.getReferenceValue((Battery) identifiable, equipmentField); case SHUNT_COMPENSATOR -> ShuntCompensatorField.getReferenceValue((ShuntCompensator) identifiable, equipmentField); case VOLTAGE_LEVEL -> VoltageLevelField.getReferenceValue((VoltageLevel) identifiable, equipmentField); case LOAD -> LoadField.getReferenceValue((Load) identifiable, equipmentField); + case TWO_WINDINGS_TRANSFORMER -> TwoWindingsTransformerField.getReferenceValue((TwoWindingsTransformer) identifiable, equipmentField); default -> throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, String.format("Unsupported equipment type : %s", identifiableType.name())); }; - - if (referenceValue == null) { - throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, - String.format("value of %s is null", equipmentField)); - } - - return referenceValue; } } diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/TwoWindingsTransformerField.java b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/TwoWindingsTransformerField.java new file mode 100644 index 000000000..e784d9744 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/TwoWindingsTransformerField.java @@ -0,0 +1,70 @@ +package org.gridsuite.modification.server.dto.formula.equipmentfield; + +import com.powsybl.iidm.network.PhaseTapChanger; +import com.powsybl.iidm.network.RatioTapChanger; +import com.powsybl.iidm.network.TwoWindingsTransformer; + +public enum TwoWindingsTransformerField { + SERIES_RESISTANCE, + SERIES_REACTANCE, + MAGNETIZING_CONDUCTANCE, + MAGNETIZING_SUSCEPTANCE, + RATED_VOLTAGE_1, + RATED_VOLTAGE_2, + 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 Double getReferenceValue(TwoWindingsTransformer transformer, String twoWindingsTransformerField) { + TwoWindingsTransformerField field = TwoWindingsTransformerField.valueOf(twoWindingsTransformerField); + final PhaseTapChanger phaseTapChanger = transformer.getPhaseTapChanger(); + final RatioTapChanger ratioTapChanger = transformer.getRatioTapChanger(); + return switch (field) { + case SERIES_RESISTANCE -> transformer.getR(); + case SERIES_REACTANCE -> transformer.getX(); + case MAGNETIZING_CONDUCTANCE -> transformer.getG(); + case MAGNETIZING_SUSCEPTANCE -> transformer.getB(); + case RATED_VOLTAGE_1 -> transformer.getRatedU1(); + case RATED_VOLTAGE_2 -> transformer.getRatedU2(); + case RATED_S -> transformer.getRatedS(); + case TARGET_V -> ratioTapChanger != null ? ratioTapChanger.getTargetV() : null; + case RATIO_LOW_TAP_POSITION -> ratioTapChanger != null ? (double) ratioTapChanger.getLowTapPosition() : null; + case RATIO_TAP_POSITION -> ratioTapChanger != null ? (double) ratioTapChanger.getTapPosition() : null; + case RATIO_TARGET_DEADBAND -> ratioTapChanger != null ? ratioTapChanger.getTargetDeadband() : null; + case REGULATION_VALUE -> phaseTapChanger != null ? phaseTapChanger.getRegulationValue() : null; + case PHASE_LOW_TAP_POSITION -> phaseTapChanger != null ? (double) phaseTapChanger.getLowTapPosition() : null; + case PHASE_TAP_POSITION -> phaseTapChanger != null ? (double) phaseTapChanger.getTapPosition() : null; + case PHASE_TARGET_DEADBAND -> phaseTapChanger != null ? phaseTapChanger.getTargetDeadband() : null; + }; + } + + public static void setNewValue(TwoWindingsTransformer transformer, String twoWindingsTransformerField, Double newValue) { + TwoWindingsTransformerField field = TwoWindingsTransformerField.valueOf(twoWindingsTransformerField); + final PhaseTapChanger phaseTapChanger = transformer.getPhaseTapChanger(); + final RatioTapChanger ratioTapChanger = transformer.getRatioTapChanger(); + + switch (field) { + case SERIES_RESISTANCE -> transformer.setR(newValue); + case SERIES_REACTANCE -> transformer.setX(newValue); + case MAGNETIZING_CONDUCTANCE -> transformer.setG(newValue); + case MAGNETIZING_SUSCEPTANCE -> transformer.setB(newValue); + case RATED_VOLTAGE_1 -> transformer.setRatedU1(newValue); + case RATED_VOLTAGE_2 -> transformer.setRatedU2(newValue); + case RATED_S -> transformer.setRatedS(newValue); + case TARGET_V -> ratioTapChanger.setTargetV(newValue); + case RATIO_LOW_TAP_POSITION -> ratioTapChanger.setLowTapPosition(newValue.intValue()); + case RATIO_TAP_POSITION -> ratioTapChanger.setTapPosition(newValue.intValue()); + case RATIO_TARGET_DEADBAND -> ratioTapChanger.setTargetDeadband(newValue); + case REGULATION_VALUE -> phaseTapChanger.setRegulationValue(newValue); + case PHASE_LOW_TAP_POSITION -> phaseTapChanger.setLowTapPosition(newValue.intValue()); + case PHASE_TAP_POSITION -> phaseTapChanger.setTapPosition(newValue.intValue()); + case PHASE_TARGET_DEADBAND -> phaseTapChanger.setTargetDeadband(newValue); + } + } +} diff --git a/src/main/java/org/gridsuite/modification/server/entities/TabularModificationEntity.java b/src/main/java/org/gridsuite/modification/server/entities/TabularModificationEntity.java index 5fec9f1c4..8694d641b 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/TabularModificationEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/TabularModificationEntity.java @@ -18,6 +18,7 @@ import org.gridsuite.modification.server.dto.*; import org.gridsuite.modification.server.entities.equipment.modification.GeneratorModificationEntity; import org.gridsuite.modification.server.entities.equipment.modification.LoadModificationEntity; +import org.gridsuite.modification.server.entities.equipment.modification.VoltageLevelModificationEntity; /** * @author Etienne Homer @@ -46,6 +47,9 @@ public TabularModificationEntity(TabularModificationInfos tabularModificationInf case "LOAD_MODIFICATION": modifications = tabularModificationInfos.getModifications().stream().map(loadModificationInfos -> new LoadModificationEntity((LoadModificationInfos) loadModificationInfos)).collect(Collectors.toList()); break; + case "VOLTAGE_LEVEL_MODIFICATION": + modifications = tabularModificationInfos.getModifications().stream().map(voltageLevelModificationInfos -> new VoltageLevelModificationEntity((VoltageLevelModificationInfos) voltageLevelModificationInfos)).collect(Collectors.toList()); + break; default: break; } @@ -76,6 +80,9 @@ public void update(@NonNull ModificationInfos modificationInfos) { case "LOAD_MODIFICATION": modifications.addAll(tabularModificationInfos.getModifications().stream().map(loadModificationInfos -> new LoadModificationEntity((LoadModificationInfos) loadModificationInfos)).collect(Collectors.toList())); break; + case "VOLTAGE_LEVEL_MODIFICATION": + modifications.addAll(tabularModificationInfos.getModifications().stream().map(voltageLevelModificationInfos -> new VoltageLevelModificationEntity((VoltageLevelModificationInfos) voltageLevelModificationInfos)).collect(Collectors.toList())); + break; default: break; } diff --git a/src/main/java/org/gridsuite/modification/server/modifications/AbstractScaling.java b/src/main/java/org/gridsuite/modification/server/modifications/AbstractScaling.java index 59aeb0895..bc966d805 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/AbstractScaling.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/AbstractScaling.java @@ -10,18 +10,15 @@ import com.powsybl.commons.reporter.TypedValue; import com.powsybl.iidm.modification.scalable.Scalable; import com.powsybl.iidm.network.Network; -import com.powsybl.network.store.iidm.impl.NetworkImpl; import org.gridsuite.modification.server.NetworkModificationException; import org.gridsuite.modification.server.dto.*; import org.gridsuite.modification.server.service.FilterService; import org.springframework.util.CollectionUtils; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Function; import java.util.stream.Collectors; import static org.gridsuite.modification.server.modifications.ModificationUtils.createReport; @@ -52,66 +49,26 @@ public void apply(Network network, Reporter subReporter) { .filter(distinctByKey(FilterInfos::getId)) .collect(Collectors.toMap(FilterInfos::getId, FilterInfos::getName)); - // export filters from filter server - String workingVariantId = network.getVariantManager().getWorkingVariantId(); - UUID uuid = ((NetworkImpl) network).getUuid(); - Map exportFilters = filterService - .exportFilters(new ArrayList<>(filters.keySet()), uuid, workingVariantId) - .stream() - .peek(t -> t.setFilterName(filters.get(t.getFilterId()))) - .collect(Collectors.toMap(FilterEquipments::getFilterId, Function.identity())); - - // 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)); - - Boolean noValidEquipmentId = exportFilters.values().stream() - .allMatch(filterEquipments -> filterEquipments.getIdentifiableAttributes().isEmpty()); - - if (noValidEquipmentId) { - String errorMsg = scalingInfos.getErrorType() + ": There is no valid equipment ID among the provided filter(s)"; - createReport(subReporter, "invalidFilters", errorMsg, TypedValue.ERROR_SEVERITY); - return; + Map exportFilters = ModificationUtils.getUuidFilterEquipmentsMap(filterService, network, subReporter, filters, scalingInfos.getErrorType()); + if (exportFilters != null) { + Map filtersWithWrongEquipmentIds = ModificationUtils.getUuidFilterWrongEquipmentsIdsMap(subReporter, exportFilters, filters); + + // apply variations + scalingInfos.getVariations().forEach(variation -> { + List identifiableAttributes = ModificationUtils.getIdentifiableAttributes(exportFilters, filtersWithWrongEquipmentIds, variation.getFilters(), subReporter); + + if (CollectionUtils.isEmpty(identifiableAttributes)) { + String filterNames = variation.getFilters().stream().map(FilterInfos::getName).collect(Collectors.joining(", ")); + createReport(subReporter, + "allFiltersWrong", + String.format("All of the following variation's filters have equipments with wrong id : %s", filterNames), + TypedValue.WARN_SEVERITY); + } else { + applyVariation(network, subReporter, identifiableAttributes, variation); + } + }); + createReport(subReporter, "scalingCreated", "new scaling created", TypedValue.INFO_SEVERITY); } - - // create report for each wrong filter - filterWithWrongEquipmentsIds.values().forEach(f -> { - var equipmentIds = String.join(", ", f.getNotFoundEquipments()); - createReport(subReporter, - "filterEquipmentsNotFound_" + f.getFilterName(), - String.format("Cannot find the following equipments %s in filter %s", equipmentIds, filters.get(f.getFilterId())), - TypedValue.WARN_SEVERITY); - }); - - // apply variations - scalingInfos.getVariations().forEach(variation -> { - variation.getFilters().stream() - .filter(f -> !exportFilters.containsKey(f.getId())) - .forEach(f -> createReport(subReporter, - "filterNotFound", - String.format("Cannot find the following filter: %s", f.getName()), - TypedValue.WARN_SEVERITY)); - - List identifiableAttributes = variation.getFilters() - .stream() - .filter(f -> !filterWithWrongEquipmentsIds.containsKey(f.getId()) && exportFilters.containsKey(f.getId())) - .flatMap(f -> exportFilters.get(f.getId()) - .getIdentifiableAttributes() - .stream()) - .collect(Collectors.toList()); - - if (CollectionUtils.isEmpty(identifiableAttributes)) { - String filterNames = variation.getFilters().stream().map(FilterInfos::getName).collect(Collectors.joining(", ")); - createReport(subReporter, - "allFiltersWrong", - String.format("All of the following variation's filters have equipments with wrong id : %s", filterNames), - TypedValue.WARN_SEVERITY); - } else { - applyVariation(network, subReporter, identifiableAttributes, variation); - } - }); - createReport(subReporter, "scalingCreated", "new scaling created", TypedValue.INFO_SEVERITY); } private void applyVariation(Network network, diff --git a/src/main/java/org/gridsuite/modification/server/modifications/BatteryModification.java b/src/main/java/org/gridsuite/modification/server/modifications/BatteryModification.java index adc3276d1..be532862e 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/BatteryModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/BatteryModification.java @@ -56,6 +56,20 @@ public void check(Network network) throws NetworkModificationException { if (!CollectionUtils.isEmpty(points) && modificationPoints != null) { ModificationUtils.getInstance().checkMaxQGreaterThanMinQ(batteryPoints, modificationPoints, 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.getActivePowerSetpoint(), + modificationInfos.getMinActivePower(), + modificationInfos.getMaxActivePower(), + battery.getMinP(), + battery.getMaxP(), + battery.getTargetP(), + exceptionType, + errorMessage + ); } @Override diff --git a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java index 6e8a62a61..aaa5ed341 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -10,26 +10,16 @@ import com.powsybl.commons.reporter.Report; import com.powsybl.commons.reporter.Reporter; import com.powsybl.commons.reporter.TypedValue; -import com.powsybl.iidm.network.Battery; -import com.powsybl.iidm.network.Generator; -import com.powsybl.iidm.network.Identifiable; -import com.powsybl.iidm.network.Load; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.ShuntCompensator; -import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.iidm.network.*; import org.gridsuite.modification.server.NetworkModificationException; import org.gridsuite.modification.server.dto.ByFormulaModificationInfos; import org.gridsuite.modification.server.dto.FilterEquipments; import org.gridsuite.modification.server.dto.FilterInfos; import org.gridsuite.modification.server.dto.formula.FormulaInfos; import org.gridsuite.modification.server.dto.formula.Operator; -import org.gridsuite.modification.server.dto.formula.equipmentfield.BatteryField; -import org.gridsuite.modification.server.dto.formula.equipmentfield.GeneratorField; -import org.gridsuite.modification.server.dto.formula.equipmentfield.LoadField; -import org.gridsuite.modification.server.dto.formula.equipmentfield.ShuntCompensatorField; -import org.gridsuite.modification.server.dto.formula.equipmentfield.VoltageLevelField; +import org.gridsuite.modification.server.dto.formula.equipmentfield.*; import org.gridsuite.modification.server.service.FilterService; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; import org.springframework.util.CollectionUtils; import java.util.ArrayList; @@ -44,9 +34,11 @@ public class ByFormulaModification extends AbstractModification { private final ByFormulaModificationInfos modificationInfos; protected FilterService filterService; + private int equipmentNotModifiedCount; public ByFormulaModification(ByFormulaModificationInfos modificationInfos) { this.modificationInfos = modificationInfos; + equipmentNotModifiedCount = 0; } @Override @@ -77,25 +69,56 @@ public void apply(Network network, Reporter subReporter) { .filter(distinctByKey(FilterInfos::getId)) .collect(Collectors.toMap(FilterInfos::getId, FilterInfos::getName)); - Map exportFilters = getUuidFilterEquipmentsMap(network, subReporter, filters); + Map exportFilters = ModificationUtils.getUuidFilterEquipmentsMap(filterService, network, subReporter, filters, modificationInfos.getErrorType()); if (exportFilters != null) { + long equipmentCount = exportFilters.values() + .stream() + .filter(filterEquipments -> !CollectionUtils.isEmpty(filterEquipments.getIdentifiableAttributes())) + .mapToLong(filterEquipments -> filterEquipments.getIdentifiableAttributes().size()) + .sum(); + long equipmentNotFoundCount = exportFilters.values() + .stream() + .filter(filterEquipments -> !CollectionUtils.isEmpty(filterEquipments.getNotFoundEquipments())) + .mapToLong(filterEquipments -> filterEquipments.getNotFoundEquipments().size()) + .sum(); Reporter formulaSubReporter = subReporter.createSubReporter("appliedFormulasModifications", "Formulas"); List formulaReports = new ArrayList<>(); modificationInfos.getFormulaInfosList().forEach(formulaInfos -> formulaInfos.getFilters().forEach(filterInfos -> applyFormulaOnFilterEquipments(network, exportFilters, formulaReports, formulaInfos, filterInfos))); - createReport(subReporter, "byFormulaModification", "new modification by formula", TypedValue.INFO_SEVERITY); - formulaSubReporter.report(Report.builder() - .withKey("appliedFormulasModifications") - .withDefaultMessage(" Formulas") - .withSeverity(TypedValue.INFO_SEVERITY) - .build()); - formulaReports.forEach(formulaSubReporter::report); + createReport(subReporter, "byFormulaModification", "New modification by formula", TypedValue.INFO_SEVERITY); + if (equipmentNotModifiedCount == 0 && equipmentNotFoundCount == 0) { + createReport(subReporter, "byFormulaModificationALL", + String.format("All equipment have been modified : %s equipment(s)", equipmentCount), + TypedValue.INFO_SEVERITY); + report(formulaSubReporter, formulaReports); + } else { + if (equipmentNotModifiedCount == equipmentCount) { + createReport(subReporter, "byFormulaModificationNone", + "No equipment have been modified", + TypedValue.ERROR_SEVERITY); + } else { + createReport(subReporter, "byFormulaModificationSome", + String.format("Some of the equipment have been modified : %s equipment(s) modified and %s equipment(s) not modified", + equipmentCount - equipmentNotModifiedCount, equipmentNotModifiedCount + equipmentNotFoundCount), + TypedValue.WARN_SEVERITY); + report(formulaSubReporter, formulaReports); + } + } } } + private void report(Reporter formulaSubReporter, List formulaReports) { + formulaSubReporter.report(Report.builder() + .withKey("appliedFormulasModifications") + .withDefaultMessage(" Formulas") + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + formulaReports.forEach(formulaSubReporter::report); + } + private void applyFormulaOnFilterEquipments(Network network, Map exportFilters, List formulaReports, @@ -103,16 +126,70 @@ private void applyFormulaOnFilterEquipments(Network network, FilterInfos filterInfos) { FilterEquipments filterEquipments = exportFilters.get(filterInfos.getId()); - formulaReports.add(Report.builder() - .withKey("byFormulaModificationFormulaFilter_" + formulaReports.size()) - .withDefaultMessage(String.format("Successful application of new modification by formula on filter %s", - filterInfos.getName())) - .withSeverity(TypedValue.INFO_SEVERITY) - .build()); + if (CollectionUtils.isEmpty(filterEquipments.getIdentifiableAttributes())) { + formulaReports.add(Report.builder() + .withKey("byFormulaModificationFormulaFilter_" + formulaReports.size()) + .withDefaultMessage(String.format("No equipments were found for filter %s", + filterInfos.getName())) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else { + List notEditableEquipments = new ArrayList<>(); + List equipmentsReport = new ArrayList<>(); + filterEquipments.getIdentifiableAttributes() + .stream() + .map(attributes -> network.getIdentifiable(attributes.getId())) + .filter(identifiable -> { + boolean isEditableEquipment = isEquipmentEditable(identifiable, formulaInfos); + if (!isEditableEquipment) { + notEditableEquipments.add(identifiable.getId()); + equipmentNotModifiedCount += 1; + } + return isEditableEquipment; + }) + .forEach(identifiable -> applyFormula(identifiable, formulaInfos, equipmentsReport, notEditableEquipments)); + + createFormulaReports(formulaReports, formulaInfos, filterInfos, filterEquipments, notEditableEquipments); + + formulaReports.addAll(equipmentsReport); + } + } + + private void createFormulaReports(List formulaReports, FormulaInfos formulaInfos, FilterInfos filterInfos, FilterEquipments filterEquipments, List notEditableEquipments) { + if (notEditableEquipments.size() == filterEquipments.getIdentifiableAttributes().size()) { + formulaReports.add(Report.builder() + .withKey("byFormulaModificationFormulaFilterFailed_" + formulaReports.size()) + .withDefaultMessage(String.format("No equipment(s) have been modified on filter %s", + filterInfos.getName())) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } else { + formulaReports.add(Report.builder() + .withKey("byFormulaModificationFormulaFilter_" + formulaReports.size()) + .withDefaultMessage(String.format("Successful application of new modification by formula on filter %s", + filterInfos.getName())) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + + formulaReports.add(Report.builder() + .withKey("numberOfValidEquipment" + formulaReports.size()) + .withDefaultMessage(String.format(" Number of equipment modified : %s", + filterEquipments.getIdentifiableAttributes().size() - notEditableEquipments.size())) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + + if (!CollectionUtils.isEmpty(notEditableEquipments)) { + formulaReports.add(Report.builder() + .withKey("NotEditedEquipmentsFilter_" + formulaReports.size()) + .withDefaultMessage(String.format(" The following equipment were not modified : %s", String.join(", ", notEditableEquipments))) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } + } formulaReports.add(Report.builder() - .withKey("numberOfValidEquipment" + formulaReports.size()) - .withDefaultMessage(String.format(" Number of equipment modified : %s", filterEquipments.getIdentifiableAttributes().size())) + .withKey("editedFieldFilter_" + formulaReports.size()) + .withDefaultMessage(String.format(" Edited field : %s", formulaInfos.getEditedField())) .withSeverity(TypedValue.INFO_SEVERITY) .build()); @@ -125,75 +202,86 @@ private void applyFormulaOnFilterEquipments(Network network, .withSeverity(TypedValue.WARN_SEVERITY) .build()); } - - formulaReports.add(Report.builder() - .withKey("editedFieldFilter_" + formulaReports.size()) - .withDefaultMessage(String.format(" Edited field : %s", formulaInfos.getEditedField())) - .withSeverity(TypedValue.INFO_SEVERITY) - .build()); - - filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, - attributes.getId(), - formulaInfos, - formulaReports)); } - @Nullable - private Map getUuidFilterEquipmentsMap(Network network, Reporter subReporter, Map filters) { - // export filters from filter server - Map exportFilters = filterService.getUuidFilterEquipmentsMap(network, filters); + private boolean isEquipmentEditable(Identifiable identifiable, + FormulaInfos formulaInfos) { + if (formulaInfos.getEditedField() == null) { + return false; + } - boolean isValidFilter = ModificationUtils.getInstance().isValidFilter(subReporter, modificationInfos.getErrorType(), exportFilters); - return isValidFilter ? exportFilters : null; + if (identifiable.getType() == IdentifiableType.TWO_WINDINGS_TRANSFORMER) { + TwoWindingsTransformerField editedField = TwoWindingsTransformerField.valueOf(formulaInfos.getEditedField()); + TwoWindingsTransformer twoWindingsTransformer = (TwoWindingsTransformer) identifiable; + return switch (editedField) { + case TARGET_V, RATIO_LOW_TAP_POSITION, RATIO_TAP_POSITION, RATIO_TARGET_DEADBAND -> twoWindingsTransformer.getRatioTapChanger() != null; + case REGULATION_VALUE, PHASE_LOW_TAP_POSITION, PHASE_TAP_POSITION, PHASE_TARGET_DEADBAND -> twoWindingsTransformer.getPhaseTapChanger() != null; + default -> true; + }; + } + return true; } - private void applyFormula(Network network, - String identifiableId, + private void applyFormula(Identifiable identifiable, FormulaInfos formulaInfos, - List reports) { - Identifiable identifiable = network.getIdentifiable(identifiableId); + List reports, + List notEditableEquipments) { Double value1 = formulaInfos.getFieldOrValue1().getRefOrValue(identifiable); Double value2 = formulaInfos.getFieldOrValue2().getRefOrValue(identifiable); - final Double newValue = applyOperation(formulaInfos.getOperator(), value1, value2); - switch (identifiable.getType()) { - case GENERATOR -> GeneratorField.setNewValue((Generator) identifiable, formulaInfos.getEditedField(), newValue); - case BATTERY -> BatteryField.setNewValue((Battery) identifiable, formulaInfos.getEditedField(), newValue); - case SHUNT_COMPENSATOR -> ShuntCompensatorField.setNewValue((ShuntCompensator) identifiable, formulaInfos.getEditedField(), newValue); - case VOLTAGE_LEVEL -> VoltageLevelField.setNewValue((VoltageLevel) identifiable, formulaInfos.getEditedField(), newValue); - case LOAD -> LoadField.setNewValue((Load) identifiable, formulaInfos.getEditedField(), newValue); - default -> throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, "Unsupported equipment"); - } - - reports.add(Report.builder() - .withKey("EquipmentModifiedReport_" + reports.size()) - .withDefaultMessage(String.format(" %s id : %s, new value of %s : %s", - modificationInfos.getIdentifiableType(), - identifiable.getId(), - formulaInfos.getEditedField(), - newValue)) - .withSeverity(TypedValue.TRACE_SEVERITY) - .build()); - } - - private Double applyOperation(Operator operator, Double value1, Double value2) { - if (value1 == null || - value2 == null) { - throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, "at least one of the value or referenced field is null"); + if (value1 == null || value2 == null) { + equipmentNotModifiedCount += 1; + notEditableEquipments.add(identifiable.getId()); + reports.add(Report.builder() + .withKey("EquipmentModifiedReportError_" + reports.size()) + .withDefaultMessage(String.format(" Cannot modify equipment %s : At least one of the value or referenced field is null", + identifiable.getId())) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + } else if (value2 == 0 && formulaInfos.getOperator() == Operator.DIVISION) { + equipmentNotModifiedCount += 1; + notEditableEquipments.add(identifiable.getId()); } else { - return switch (operator) { - case ADDITION -> value1 + value2; - case SUBTRACTION -> value1 - value2; - case MULTIPLICATION -> value1 * value2; - case DIVISION -> { - if (value2 == 0) { - throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, - "there is a division by zero in a formula"); - } else { - yield value1 / value2; - } + try { + final Double newValue = applyOperation(formulaInfos.getOperator(), value1, value2); + switch (identifiable.getType()) { + case GENERATOR -> GeneratorField.setNewValue((Generator) identifiable, formulaInfos.getEditedField(), newValue); + case BATTERY -> BatteryField.setNewValue((Battery) identifiable, formulaInfos.getEditedField(), newValue); + case SHUNT_COMPENSATOR -> ShuntCompensatorField.setNewValue((ShuntCompensator) identifiable, formulaInfos.getEditedField(), newValue); + case VOLTAGE_LEVEL -> VoltageLevelField.setNewValue((VoltageLevel) identifiable, formulaInfos.getEditedField(), newValue); + case LOAD -> LoadField.setNewValue((Load) identifiable, formulaInfos.getEditedField(), newValue); + case TWO_WINDINGS_TRANSFORMER -> TwoWindingsTransformerField.setNewValue((TwoWindingsTransformer) identifiable, formulaInfos.getEditedField(), newValue); + default -> throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, "Unsupported equipment"); } - case PERCENTAGE -> value1 * (value2 / 100); - }; + reports.add(Report.builder() + .withKey("EquipmentModifiedReport_" + reports.size()) + .withDefaultMessage(String.format(" %s id : %s, new value of %s : %s", + modificationInfos.getIdentifiableType(), + identifiable.getId(), + formulaInfos.getEditedField(), + newValue)) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + } catch (Exception e) { + notEditableEquipments.add(identifiable.getId()); + equipmentNotModifiedCount += 1; + reports.add(Report.builder() + .withKey("EquipmentModifiedReportExceptionf_" + reports.size()) + .withDefaultMessage(String.format(" Cannot modify equipment %s : %s", + identifiable.getId(), + e.getMessage())) + .withSeverity(TypedValue.TRACE_SEVERITY) + .build()); + } } } + + private Double applyOperation(Operator operator, @NotNull Double value1, @NotNull Double value2) { + return switch (operator) { + case ADDITION -> value1 + value2; + case SUBTRACTION -> value1 - value2; + case MULTIPLICATION -> value1 * value2; + case DIVISION -> value1 / value2; + case PERCENTAGE -> value1 * (value2 / 100); + }; + } } diff --git a/src/main/java/org/gridsuite/modification/server/modifications/GenerationDispatch.java b/src/main/java/org/gridsuite/modification/server/modifications/GenerationDispatch.java index d563ad9fc..a835c3e2b 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/GenerationDispatch.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/GenerationDispatch.java @@ -82,6 +82,14 @@ private static double computeTotalDemand(Component component, double lossCoeffic 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, Reporter reporter) { double totalAmountFixedSupply = 0.; List generatorsWithoutSetpointList = new ArrayList<>(); @@ -306,7 +314,7 @@ public void endReport(List adjustableGenerators) { report(reporter, 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(reporter, suffixKey, "GeneratorSetTargetP", "The active power set point of generator ${generator} has been set to ${newValue} MW", - Map.of(GENERATOR, g.getId(), "newValue", g.getTargetP()), TypedValue.TRACE_SEVERITY)); + Map.of(GENERATOR, g.getId(), "newValue", round(g.getTargetP())), TypedValue.TRACE_SEVERITY)); // report unchanged generators int nbUnchangedGenerators = adjustableGenerators.size() - updatedGenerators.size(); @@ -493,26 +501,30 @@ public void apply(Network network, Reporter subReporter) { // get total value of connected loads in the connected component double totalDemand = computeTotalDemand(component, generationDispatchInfos.getLossCoefficient()); report(powerToDispatchReporter, Integer.toString(componentNum), "TotalDemand", "The total demand is : ${totalDemand} MW", - Map.of("totalDemand", totalDemand), TypedValue.INFO_SEVERITY); + Map.of("totalDemand", round(totalDemand)), TypedValue.INFO_SEVERITY); // get total supply value for generators with fixed supply double totalAmountFixedSupply = computeTotalAmountFixedSupply(network, component, generatorsWithFixedSupply, powerToDispatchReporter); report(powerToDispatchReporter, Integer.toString(componentNum), "TotalAmountFixedSupply", "The total amount of fixed supply is : ${totalAmountFixedSupply} MW", - Map.of("totalAmountFixedSupply", totalAmountFixedSupply), TypedValue.INFO_SEVERITY); + Map.of("totalAmountFixedSupply", round(totalAmountFixedSupply)), TypedValue.INFO_SEVERITY); // compute hvdc balance to other synchronous components double hvdcBalance = computeHvdcBalance(component); report(powerToDispatchReporter, Integer.toString(componentNum), "TotalOutwardHvdcFlow", "The HVDC balance is : ${hvdcBalance} MW", - Map.of("hvdcBalance", hvdcBalance), TypedValue.INFO_SEVERITY); + Map.of("hvdcBalance", round(hvdcBalance)), TypedValue.INFO_SEVERITY); - double totalAmountSupplyToBeDispatched = totalDemand - totalAmountFixedSupply - hvdcBalance; + double activeBatteryTotalTargetP = computeTotalActiveBatteryTargetP(component); + report(powerToDispatchReporter, 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(powerToDispatchReporter, Integer.toString(componentNum), "TotalAmountFixedSupplyExceedsTotalDemand", "The total amount of fixed supply exceeds the total demand", Map.of(), TypedValue.WARN_SEVERITY); continue; } else { report(powerToDispatchReporter, Integer.toString(componentNum), "TotalAmountSupplyToBeDispatched", "The total amount of supply to be dispatched is : ${totalAmountSupplyToBeDispatched} MW", - Map.of("totalAmountSupplyToBeDispatched", totalAmountSupplyToBeDispatched), TypedValue.INFO_SEVERITY); + Map.of("totalAmountSupplyToBeDispatched", round(totalAmountSupplyToBeDispatched)), TypedValue.INFO_SEVERITY); } // get adjustable generators in the component @@ -548,12 +560,22 @@ public void apply(Network network, Reporter subReporter) { report(resultReporter, Integer.toString(componentNum), "SupplyDemandBalanceCouldBeMet", "The supply-demand balance could be met", Map.of(), TypedValue.INFO_SEVERITY); - generatorsByRegion.forEach((region, generators) -> report(resultReporter, Integer.toString(componentNum), "SumGeneratorActivePower" + region, "Sum of generator active power setpoints in ${region} region: ${sum} MW.", - Map.of("region", region, "sum", getSumActivePower(generators)), 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", remainingPowerImbalance), TypedValue.WARN_SEVERITY); + Map.of("remainingPower", round(remainingPowerImbalance)), TypedValue.WARN_SEVERITY); } } } @@ -602,8 +624,11 @@ private boolean hasCvgPropertyName(Set propertyNames) { return propertyNames.stream().anyMatch(REGION_CVG::equals); } - private double getSumActivePower(List generators) { - return generators.stream().mapToDouble(Generator::getTargetP).sum(); + 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.; + } } diff --git a/src/main/java/org/gridsuite/modification/server/modifications/GeneratorModification.java b/src/main/java/org/gridsuite/modification/server/modifications/GeneratorModification.java index 25c95d89d..5be503c7a 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/GeneratorModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/GeneratorModification.java @@ -67,9 +67,23 @@ public void check(Network network) throws NetworkModificationException { 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.getActivePowerSetpoint(), + modificationInfos.getMinActivePower(), + modificationInfos.getMaxActivePower(), + generator.getMinP(), + generator.getMaxP(), + generator.getTargetP(), + exceptionType, + errorMessage + ); + } + @Override public void apply(Network network, Reporter subReporter) { if (modificationInfos == null) { diff --git a/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java b/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java index bbc4f1f0b..f9de96006 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java @@ -19,6 +19,8 @@ import com.powsybl.iidm.network.extensions.IdentifiableShortCircuitAdder; import org.gridsuite.modification.server.NetworkModificationException; import org.gridsuite.modification.server.dto.*; +import org.gridsuite.modification.server.service.FilterService; +import org.jetbrains.annotations.Nullable; import org.springframework.util.CollectionUtils; import java.util.*; @@ -27,6 +29,7 @@ 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 static org.gridsuite.modification.server.NetworkModificationException.Type.*; @@ -780,6 +783,41 @@ public void modifyMinMaxReactiveLimits(MinMaxReactiveLimits minMaxReactiveLimits reportModifications(subReporterReactiveLimits, 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()); + reports.add(buildModificationReport(oldParticipate, info.getValue(), "Participate")); + }); + + Optional.ofNullable(droopInfo).ifPresent(info -> { + activePowerControl.setDroop(info.getValue()); + reports.add(buildModificationReport(oldDroop, info.getValue(), "Droop")); + }); + } + + private void createNewActivePowerControl(ActivePowerControlAdder adder, + AttributeModification participateInfo, + AttributeModification droopInfo, + List reports) { + boolean participate = participateInfo != null ? participateInfo.getValue() : false; + adder.withParticipate(participate); + if (participateInfo != null) { + reports.add(buildModificationReport(null, participate, "Participate")); + } + double droop = droopInfo != null ? droopInfo.getValue() : Double.NaN; + adder.withDroop(droop); + if (droopInfo != null) { + reports.add(buildModificationReport(Double.NaN, droop, "Droop")); + } + adder.add(); + } + public Reporter modifyActivePowerControlAttributes(ActivePowerControl activePowerControl, ActivePowerControlAdder activePowerControlAdder, AttributeModification participateInfo, @@ -787,37 +825,11 @@ public Reporter modifyActivePowerControlAttributes(ActivePowerControl activeP Reporter subReporter, Reporter subReporterSetpoints) { List reports = new ArrayList<>(); - double oldDroop = Double.NaN; - boolean oldParticipate = false; - double droop = droopInfo != null ? droopInfo.getValue() : Double.NaN; if (activePowerControl != null) { - oldDroop = activePowerControl.getDroop(); - oldParticipate = activePowerControl.isParticipate(); - } - - if (participateInfo != null) { - activePowerControlAdder - .withParticipate(participateInfo.getValue()); - reports.add(ModificationUtils.getInstance().buildModificationReport(activePowerControl != null ? activePowerControl.isParticipate() : null, - participateInfo.getValue(), - "Participate")); - } else { - activePowerControlAdder - .withParticipate(oldParticipate); - } - - if (droopInfo != null) { - activePowerControlAdder - .withDroop(droop); - reports.add(ModificationUtils.getInstance().buildModificationReport(oldDroop, - droop, - "Droop")); + modifyExistingActivePowerControl(activePowerControl, participateInfo, droopInfo, reports); } else { - activePowerControlAdder - .withDroop(oldDroop); + createNewActivePowerControl(activePowerControlAdder, participateInfo, droopInfo, reports); } - activePowerControlAdder - .add(); Reporter subReporterSetpoints2 = subReporterSetpoints; if (subReporterSetpoints == null && !reports.isEmpty()) { @@ -862,6 +874,16 @@ public void checkMaxReactivePowerGreaterThanMinReactivePower(MinMaxReactiveLimit } } + 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, @@ -998,7 +1020,7 @@ public boolean isValidFilter(Reporter subReporter, NetworkModificationException.Type errorType, Map exportFilters) { boolean noValidEquipmentId = exportFilters.values().stream() - .allMatch(filterEquipments -> filterEquipments.getIdentifiableAttributes().isEmpty()); + .allMatch(filterEquipments -> CollectionUtils.isEmpty(filterEquipments.getIdentifiableAttributes())); if (noValidEquipmentId) { String errorMsg = errorType + ": There is no valid equipment ID among the provided filter(s)"; @@ -1008,5 +1030,47 @@ public boolean isValidFilter(Reporter subReporter, return true; } + + public static List getIdentifiableAttributes(Map exportFilters, Map filtersWithWrongEquipmentIds, List filterInfos, Reporter subReporter) { + filterInfos.stream() + .filter(f -> !exportFilters.containsKey(f.getId())) + .forEach(f -> createReport(subReporter, + "filterNotFound", + String.format("Cannot find the following filter: %s", 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(FilterService filterService, Network network, Reporter subReporter, Map filters, NetworkModificationException.Type errorType) { + Map exportFilters = filterService.getUuidFilterEquipmentsMap(network, filters); + + boolean isValidFilter = ModificationUtils.getInstance().isValidFilter(subReporter, errorType, exportFilters); + return isValidFilter ? exportFilters : null; + } + + public static Map getUuidFilterWrongEquipmentsIdsMap(Reporter subReporter, 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(subReporter, + "filterEquipmentsNotFound_" + f.getFilterName(), + String.format("Cannot find the following equipments %s in filter %s", equipmentIds, filters.get(f.getFilterId())), + TypedValue.WARN_SEVERITY); + }); + return filterWithWrongEquipmentsIds; + } } diff --git a/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java b/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java index 928cda21b..e25fdcf17 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java @@ -6,6 +6,7 @@ */ package org.gridsuite.modification.server.modifications; +import com.google.common.collect.Iterables; import com.powsybl.iidm.network.*; import com.powsybl.network.store.client.NetworkStoreService; import lombok.Setter; @@ -40,6 +41,8 @@ public class NetworkStoreListener implements NetworkListener { private final List createdEquipments = new ArrayList<>(); + private final List modifiedEquipments = new ArrayList<>(); + private final Set networkImpacts = new LinkedHashSet<>(); // TODO : Move to the NetworkModificationApplicator class @@ -92,18 +95,6 @@ public Network getNetwork() { return network; } - @Override - public void onUpdate(Identifiable identifiable, String attribute, Object oldValue, Object newValue) { - networkImpacts.add( - SimpleElementImpact.builder() - .impactType(SimpleElementImpact.SimpleImpactType.MODIFICATION) - .elementType(identifiable.getType()) - .elementId(identifiable.getId()) - .substationIds(getSubstationIds(identifiable)) - .build() - ); - } - private void addSimpleModificationImpact(Identifiable identifiable) { networkImpacts.add( SimpleElementImpact.builder() @@ -130,9 +121,66 @@ public void onElementReplaced(Identifiable identifiable, String attribute, Objec addSimpleModificationImpact(identifiable); } + @Override + public void onUpdate(Identifiable identifiable, String attribute, Object oldValue, Object newValue) { + addSimpleModificationImpact(identifiable); + updateEquipmentIndexation(identifiable, attribute, networkUuid, network.getVariantManager().getWorkingVariantId()); + } + @Override public void onUpdate(Identifiable identifiable, String attribute, String variantId, Object oldValue, Object newValue) { addSimpleModificationImpact(identifiable); + updateEquipmentIndexation(identifiable, attribute, networkUuid, network.getVariantManager().getWorkingVariantId()); + } + + private void updateEquipmentIndexation(Identifiable identifiable, String attribute, UUID networkUuid, String variantId) { + modifiedEquipments.add(toEquipmentInfos(identifiable, networkUuid, variantId)); + + // because all each equipment carry its linked voltage levels/substations name within its document + // if attribute is "name" and identifiable type is VOLTAGE_LEVEL or SUBSTATION, we need to update all equipments linked to it + if (attribute.equals("name") && (identifiable.getType().equals(IdentifiableType.VOLTAGE_LEVEL) || identifiable.getType().equals(IdentifiableType.SUBSTATION))) { + updateLinkedEquipments(identifiable); + } + } + + private void updateLinkedEquipments(Identifiable identifiable) { + if (identifiable.getType().equals(IdentifiableType.VOLTAGE_LEVEL)) { + VoltageLevel updatedVoltageLevel = network.getVoltageLevel(identifiable.getId()); + // update all equipments linked to voltageLevel + updateEquipmentsLinkedToVoltageLevel(updatedVoltageLevel); + // update substation linked to voltageLevel + Optional linkedSubstation = updatedVoltageLevel.getSubstation(); + if (linkedSubstation.isPresent()) { + modifiedEquipments.add(toEquipmentInfos(linkedSubstation.get(), networkUuid, network.getVariantManager().getWorkingVariantId())); + } + } else if (identifiable.getType().equals(IdentifiableType.SUBSTATION)) { + Substation updatedSubstation = network.getSubstation(identifiable.getId()); + updateEquipmentsLinkedToSubstation(updatedSubstation); + } + } + + private void updateEquipmentsLinkedToSubstation(Substation substation) { + Iterable linkedVoltageLevels = substation.getVoltageLevels(); + // update all voltageLevels linked to substation + linkedVoltageLevels.forEach(vl -> modifiedEquipments.add(toEquipmentInfos(vl, networkUuid, network.getVariantManager().getWorkingVariantId()))); + // update all equipments linked to each of the voltageLevels + linkedVoltageLevels.forEach(vl -> + Iterables.concat( + vl.getConnectables(), + vl.getSwitches() + ).forEach(c -> + modifiedEquipments.add(toEquipmentInfos(c, networkUuid, network.getVariantManager().getWorkingVariantId())) + ) + ); + } + + private void updateEquipmentsLinkedToVoltageLevel(VoltageLevel voltageLevel) { + Iterables.concat( + voltageLevel.getConnectables(), + voltageLevel.getSwitches() + ).forEach(c -> + modifiedEquipments.add(toEquipmentInfos(c, networkUuid, network.getVariantManager().getWorkingVariantId())) + ); } @Override @@ -192,6 +240,18 @@ public NetworkModificationResult flushNetworkModifications() { .build(); } + private static EquipmentInfos toEquipmentInfos(Identifiable identifiable, UUID networkUuid, String variantId) { + return EquipmentInfos.builder() + .networkUuid(networkUuid) + .variantId(variantId) + .id(identifiable.getId()) + .name(identifiable.getNameOrId()) + .type(identifiable.getType().name()) + .voltageLevels(EquipmentInfos.getVoltageLevelsInfos(identifiable)) + .substations(EquipmentInfos.getSubstationsInfos(identifiable)) + .build(); + } + private void flushEquipmentInfos() { String variantId = network.getVariantManager().getWorkingVariantId(); Set presentEquipmentDeletionsIds = equipmentInfosService.findEquipmentInfosList(deletedEquipmentsIds, networkUuid, variantId).stream().map(EquipmentInfos::getId).collect(Collectors.toSet()); @@ -213,5 +273,6 @@ private void flushEquipmentInfos() { equipmentInfosService.deleteEquipmentInfosList(equipmentDeletionsIds, networkUuid, variantId); equipmentInfosService.addAllTombstonedEquipmentInfos(tombstonedEquipmentInfos); equipmentInfosService.addAllEquipmentInfos(createdEquipments); + equipmentInfosService.addAllEquipmentInfos(modifiedEquipments); } } diff --git a/src/main/java/org/gridsuite/modification/server/modifications/TabularModification.java b/src/main/java/org/gridsuite/modification/server/modifications/TabularModification.java index 2296e8e14..b5f992747 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/TabularModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/TabularModification.java @@ -66,6 +66,9 @@ public void apply(Network network, Reporter subReporter) { case "LOAD_MODIFICATION": defaultMessage = "loads" + defaultMessage; break; + case "VOLTAGE_LEVEL_MODIFICATION": + defaultMessage = "voltage levels" + defaultMessage; + break; default: defaultMessage = "equipments of unknown type" + defaultMessage; break; diff --git a/src/main/java/org/gridsuite/modification/server/modifications/VoltageInitModification.java b/src/main/java/org/gridsuite/modification/server/modifications/VoltageInitModification.java index 782ba1551..38a84d4f7 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/VoltageInitModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/VoltageInitModification.java @@ -26,14 +26,18 @@ public class VoltageInitModification extends AbstractModification { private final VoltageInitModificationInfos voltageInitModificationInfos; - private static final String GENERATOR_KEY = "GeneratorModifications"; - private static final String GENERATOR_NAME = "Generator ${generatorId}"; - private static final String STATIC_VAR_COMPENSATOR_KEY = "StaticVarCompensatorModifications"; - private static final String STATIC_VAR_COMPENSATOR_NAME = "Static var compensator ${compensatorId}"; - private static final String VSC_CONVERTER_STATION_KEY = "VscConverterStationModifications"; - private static final String VSC_CONVERTER_STATION_NAME = "Vsc converter station ${converterId}"; - private static final String SHUNT_COMPENSATOR_KEY = "ShuntCompensatorModifications"; - private static final String SHUNT_COMPENSATOR_NAME = "Shunt compensator ${compensatorId}"; + 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 REACTIVE_POWER_SET_POINT = "Reactive power set point"; @@ -66,43 +70,48 @@ public void apply(Network network, Reporter subReporter) { private void applyGeneratorModification(Network network, Reporter subReporter) { int modificationsCount = 0; + List reports = new ArrayList<>(); for (final VoltageInitGeneratorModificationInfos m : voltageInitModificationInfos.getGenerators()) { final Generator generator = network.getGenerator(m.getGeneratorId()); if (generator == null) { - Reporter reporter = subReporter.createSubReporter(GENERATOR_KEY, GENERATOR_NAME, "generatorId", m.getGeneratorId()); - reporter.report(Report.builder().withKey("generatorNotFound") - .withDefaultMessage("Generator with id=${id} not found") - .withValue("id", m.getGeneratorId()) - .withSeverity(TypedValue.WARN_SEVERITY).build()); + reports.add(Report.builder().withKey("generatorNotFound") + .withDefaultMessage("Generator with id=${id} not found") + .withValue("id", m.getGeneratorId()) + .withSeverity(TypedValue.WARN_SEVERITY).build()); } else if (m.getVoltageSetpoint() != null || m.getReactivePowerSetpoint() != null) { modificationsCount++; - Reporter reporter = subReporter.createSubReporter(GENERATOR_KEY, GENERATOR_NAME, "generatorId", m.getGeneratorId()); - reporter.report(Report.builder().withKey("generatorModification") - .withDefaultMessage("Generator with id=${id} modified :") - .withValue("id", m.getGeneratorId()) - .withSeverity(TypedValue.TRACE_SEVERITY).build()); + reports.add(Report.builder().withKey("generatorModification") + .withDefaultMessage("Generator with id=${id} modified :") + .withValue("id", m.getGeneratorId()) + .withSeverity(TypedValue.TRACE_SEVERITY).build()); if (m.getVoltageSetpoint() != null) { final double oldTargetV = generator.getTargetV(); generator.setTargetV(m.getVoltageSetpoint()); - reporter.report(ModificationUtils.buildModificationReport(oldTargetV, m.getVoltageSetpoint(), VOLTAGE_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + reports.add(ModificationUtils.buildModificationReport(oldTargetV, m.getVoltageSetpoint(), VOLTAGE_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); } if (m.getReactivePowerSetpoint() != null) { final double oldTargetQ = generator.getTargetQ(); generator.setTargetQ(m.getReactivePowerSetpoint()); - reporter.report(ModificationUtils.buildModificationReport(oldTargetQ, m.getReactivePowerSetpoint(), REACTIVE_POWER_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + reports.add(ModificationUtils.buildModificationReport(oldTargetQ, m.getReactivePowerSetpoint(), REACTIVE_POWER_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); } } } + if (!reports.isEmpty()) { + Reporter generatorsReporter = subReporter.createSubReporter(GENERATORS_KEY, GENERATORS_NAME); + reports.forEach(generatorsReporter::report); + } if (modificationsCount > 0) { subReporter.report(new Report("generatorModificationsResume", "${count} generator(s) have been modified.", Map.of( - "count", new TypedValue(modificationsCount, TypedValue.UNTYPED), - Report.REPORT_SEVERITY_KEY, TypedValue.INFO_SEVERITY + "count", new TypedValue(modificationsCount, TypedValue.UNTYPED), + Report.REPORT_SEVERITY_KEY, TypedValue.INFO_SEVERITY ))); } } private void applyTransformerModification(Network network, Reporter subReporter) { int modificationsCount = 0; + List reports2WT = new ArrayList<>(); + List reports3WT = new ArrayList<>(); for (final VoltageInitTransformerModificationInfos t : voltageInitModificationInfos.getTransformers()) { if (t.getRatioTapChangerPosition() == null) { continue; @@ -110,197 +119,212 @@ private void applyTransformerModification(Network network, Reporter subReporter) modificationsCount++; if (t.getLegSide() != null) { final ThreeWindingsTransformer threeWindingsTransformer = network.getThreeWindingsTransformer(t.getTransformerId()); - Reporter reporter = subReporter.createSubReporter("3WindingsTransformerModifications", "3 windings transformer ${transformerId}", "transformerId", t.getTransformerId()); if (threeWindingsTransformer == null) { - reporter.report(Report.builder().withKey("3WindingsTransformerNotFound") - .withDefaultMessage("3 windings transformer with id=${id} not found") - .withValue("id", t.getTransformerId()) - .withSeverity(TypedValue.WARN_SEVERITY).build()); + reports3WT.add(Report.builder().withKey("3WindingsTransformerNotFound") + .withDefaultMessage("3 windings transformer with id=${id} not found") + .withValue("id", t.getTransformerId()) + .withSeverity(TypedValue.WARN_SEVERITY).build()); } else if (threeWindingsTransformer.getLeg(t.getLegSide()).getRatioTapChanger() == null) { - reporter.report(Report.builder().withKey("3WindingsTransformerRatioTapChangerNotFound") - .withDefaultMessage("3 windings transformer with id=${id} : Ratio tap changer for leg ${leg} not found") - .withValue("id", t.getTransformerId()) - .withValue("leg", t.getLegSide().name()) - .withSeverity(TypedValue.WARN_SEVERITY).build()); + reports3WT.add(Report.builder().withKey("3WindingsTransformerRatioTapChangerNotFound") + .withDefaultMessage("3 windings transformer with id=${id} : Ratio tap changer for leg ${leg} not found") + .withValue("id", t.getTransformerId()) + .withValue("leg", t.getLegSide().name()) + .withSeverity(TypedValue.WARN_SEVERITY).build()); } else { - reporter.report(Report.builder().withKey("3WindingsTransformerModification") - .withDefaultMessage("3 windings transformer with id=${id} modified :") - .withValue("id", t.getTransformerId()) - .withSeverity(TypedValue.TRACE_SEVERITY).build()); + reports3WT.add(Report.builder().withKey("3WindingsTransformerModification") + .withDefaultMessage("3 windings transformer with id=${id} modified :") + .withValue("id", t.getTransformerId()) + .withSeverity(TypedValue.TRACE_SEVERITY).build()); final int oldTapPosition = threeWindingsTransformer.getLeg(t.getLegSide()).getRatioTapChanger().getTapPosition(); threeWindingsTransformer.getLeg(t.getLegSide()).getRatioTapChanger().setTapPosition(t.getRatioTapChangerPosition()); - reporter.report(ModificationUtils.buildModificationReport(oldTapPosition, t.getRatioTapChangerPosition(), "Leg " + t.getLegSide().name() + " ratio tap changer position", 1, TypedValue.TRACE_SEVERITY)); + reports3WT.add(ModificationUtils.buildModificationReport(oldTapPosition, t.getRatioTapChangerPosition(), "Leg " + t.getLegSide().name() + " ratio tap changer position", 1, TypedValue.TRACE_SEVERITY)); } } else { final TwoWindingsTransformer twoWindingsTransformer = network.getTwoWindingsTransformer(t.getTransformerId()); - Reporter reporter = subReporter.createSubReporter("2WindingsTransformerModifications", "2 windings transformer ${transformerId}", "transformerId", t.getTransformerId()); if (twoWindingsTransformer == null) { - reporter.report(Report.builder().withKey("2WindingsTransformerNotFound") - .withDefaultMessage("2 windings transformer with id=${id} not found") - .withValue("id", t.getTransformerId()) - .withSeverity(TypedValue.WARN_SEVERITY).build()); + reports2WT.add(Report.builder().withKey("2WindingsTransformerNotFound") + .withDefaultMessage("2 windings transformer with id=${id} not found") + .withValue("id", t.getTransformerId()) + .withSeverity(TypedValue.WARN_SEVERITY).build()); } else if (twoWindingsTransformer.getRatioTapChanger() == null) { - reporter.report(Report.builder().withKey("2WindingsTransformerRatioTapChangerNotFound") - .withDefaultMessage("2 windings transformer with id=${id} : Ratio tap changer not found") - .withValue("id", t.getTransformerId()) - .withSeverity(TypedValue.WARN_SEVERITY).build()); + reports2WT.add(Report.builder().withKey("2WindingsTransformerRatioTapChangerNotFound") + .withDefaultMessage("2 windings transformer with id=${id} : Ratio tap changer not found") + .withValue("id", t.getTransformerId()) + .withSeverity(TypedValue.WARN_SEVERITY).build()); } else { - reporter.report(Report.builder().withKey("2WindingsTransformerModification") - .withDefaultMessage("2 windings transformer with id=${id} modified :") - .withValue("id", t.getTransformerId()) - .withSeverity(TypedValue.TRACE_SEVERITY).build()); + reports2WT.add(Report.builder().withKey("2WindingsTransformerModification") + .withDefaultMessage("2 windings transformer with id=${id} modified :") + .withValue("id", t.getTransformerId()) + .withSeverity(TypedValue.TRACE_SEVERITY).build()); final int oldTapPosition = twoWindingsTransformer.getRatioTapChanger().getTapPosition(); twoWindingsTransformer.getRatioTapChanger().setTapPosition(t.getRatioTapChangerPosition()); - reporter.report(ModificationUtils.buildModificationReport(oldTapPosition, t.getRatioTapChangerPosition(), "Ratio tap changer position", 1, TypedValue.TRACE_SEVERITY)); + reports2WT.add(ModificationUtils.buildModificationReport(oldTapPosition, t.getRatioTapChangerPosition(), "Ratio tap changer position", 1, TypedValue.TRACE_SEVERITY)); } } } + if (!reports2WT.isEmpty()) { + Reporter twoWindingsTransformerReporter = subReporter.createSubReporter(TWO_WINDINGS_TRANSFORMERS_KEY, TWO_WINDINGS_TRANSFORMERS_NAME); + reports2WT.forEach(twoWindingsTransformerReporter::report); + } + if (!reports3WT.isEmpty()) { + Reporter threeWindingsTransformerReporter = subReporter.createSubReporter(THREE_WINDINGS_TRANSFORMERS_KEY, THREE_WINDINGS_TRANSFORMERS_NAME); + reports3WT.forEach(threeWindingsTransformerReporter::report); + } if (modificationsCount > 0) { subReporter.report(new Report("windingsTransformerModificationsResume", "${count} transformer(s) have been modified.", Map.of( - "count", new TypedValue(modificationsCount, TypedValue.UNTYPED), - Report.REPORT_SEVERITY_KEY, TypedValue.INFO_SEVERITY + "count", new TypedValue(modificationsCount, TypedValue.UNTYPED), + Report.REPORT_SEVERITY_KEY, TypedValue.INFO_SEVERITY ))); } } private void applyStaticVarCompensatorModification(Network network, Reporter subReporter) { int modificationsCount = 0; + List reports = new ArrayList<>(); for (VoltageInitStaticVarCompensatorModificationInfos s : voltageInitModificationInfos.getStaticVarCompensators()) { final StaticVarCompensator staticVarCompensator = network.getStaticVarCompensator(s.getStaticVarCompensatorId()); if (staticVarCompensator == null) { - Reporter reporter = subReporter.createSubReporter(STATIC_VAR_COMPENSATOR_KEY, STATIC_VAR_COMPENSATOR_NAME, "compensatorId", s.getStaticVarCompensatorId()); - reporter.report(Report.builder().withKey("staticVarCompensatorNotFound") - .withDefaultMessage("Static var compensator with id=${id} not found") - .withValue("id", s.getStaticVarCompensatorId()) - .withSeverity(TypedValue.WARN_SEVERITY).build()); + reports.add(Report.builder().withKey("staticVarCompensatorNotFound") + .withDefaultMessage("Static var compensator with id=${id} not found") + .withValue("id", s.getStaticVarCompensatorId()) + .withSeverity(TypedValue.WARN_SEVERITY).build()); } else if (s.getVoltageSetpoint() != null || s.getReactivePowerSetpoint() != null) { modificationsCount++; - Reporter reporter = subReporter.createSubReporter(STATIC_VAR_COMPENSATOR_KEY, STATIC_VAR_COMPENSATOR_NAME, "compensatorId", s.getStaticVarCompensatorId()); - reporter.report(Report.builder().withKey("staticVarCompensatorModification") - .withDefaultMessage("Static var compensator with id=${id} modified :") - .withValue("id", s.getStaticVarCompensatorId()) - .withSeverity(TypedValue.TRACE_SEVERITY).build()); + reports.add(Report.builder().withKey("staticVarCompensatorModification") + .withDefaultMessage("Static var compensator with id=${id} modified :") + .withValue("id", s.getStaticVarCompensatorId()) + .withSeverity(TypedValue.TRACE_SEVERITY).build()); if (s.getVoltageSetpoint() != null) { final double oldTargetV = staticVarCompensator.getVoltageSetpoint(); staticVarCompensator.setVoltageSetpoint(s.getVoltageSetpoint()); - reporter.report(ModificationUtils.buildModificationReport(oldTargetV, s.getVoltageSetpoint(), VOLTAGE_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + 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()); - reporter.report(ModificationUtils.buildModificationReport(oldTargetQ, s.getReactivePowerSetpoint(), REACTIVE_POWER_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + reports.add(ModificationUtils.buildModificationReport(oldTargetQ, s.getReactivePowerSetpoint(), REACTIVE_POWER_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); } } } + if (!reports.isEmpty()) { + Reporter staticVarsReporter = subReporter.createSubReporter(STATIC_VAR_COMPENSATORS_KEY, STATIC_VAR_COMPENSATORS_NAME); + reports.forEach(staticVarsReporter::report); + } if (modificationsCount > 0) { subReporter.report(new Report("svcModificationsResume", "${count} static var compensator(s) have been modified.", Map.of( - "count", new TypedValue(modificationsCount, TypedValue.UNTYPED), - Report.REPORT_SEVERITY_KEY, TypedValue.INFO_SEVERITY + "count", new TypedValue(modificationsCount, TypedValue.UNTYPED), + Report.REPORT_SEVERITY_KEY, TypedValue.INFO_SEVERITY ))); } } private void applyVscConverterStationModification(Network network, Reporter subReporter) { int modificationsCount = 0; + List reports = new ArrayList<>(); for (VoltageInitVscConverterStationModificationInfos v : voltageInitModificationInfos.getVscConverterStations()) { final VscConverterStation vscConverterStation = network.getVscConverterStation(v.getVscConverterStationId()); if (vscConverterStation == null) { - Reporter reporter = subReporter.createSubReporter(VSC_CONVERTER_STATION_KEY, VSC_CONVERTER_STATION_NAME, "converterId", v.getVscConverterStationId()); - reporter.report(Report.builder().withKey("vscConverterStationNotFound") - .withDefaultMessage("Vsc converter station with id=${id} not found") - .withValue("id", v.getVscConverterStationId()) - .withSeverity(TypedValue.WARN_SEVERITY).build()); + reports.add(Report.builder().withKey("vscConverterStationNotFound") + .withDefaultMessage("Vsc converter station with id=${id} not found") + .withValue("id", v.getVscConverterStationId()) + .withSeverity(TypedValue.WARN_SEVERITY).build()); } else if (v.getVoltageSetpoint() != null || v.getReactivePowerSetpoint() != null) { modificationsCount++; - Reporter reporter = subReporter.createSubReporter(VSC_CONVERTER_STATION_KEY, VSC_CONVERTER_STATION_NAME, "converterId", v.getVscConverterStationId()); - reporter.report(Report.builder().withKey("vscConverterStationModification") - .withDefaultMessage("Vsc converter station with id=${id} modified :") - .withValue("id", v.getVscConverterStationId()) - .withSeverity(TypedValue.TRACE_SEVERITY).build()); + reports.add(Report.builder().withKey("vscConverterStationModification") + .withDefaultMessage("Vsc converter station with id=${id} modified :") + .withValue("id", v.getVscConverterStationId()) + .withSeverity(TypedValue.TRACE_SEVERITY).build()); if (v.getVoltageSetpoint() != null) { final double oldTargetV = vscConverterStation.getVoltageSetpoint(); vscConverterStation.setVoltageSetpoint(v.getVoltageSetpoint()); - reporter.report(ModificationUtils.buildModificationReport(oldTargetV, v.getVoltageSetpoint(), VOLTAGE_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + 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()); - reporter.report(ModificationUtils.buildModificationReport(oldTargetQ, v.getReactivePowerSetpoint(), REACTIVE_POWER_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); + reports.add(ModificationUtils.buildModificationReport(oldTargetQ, v.getReactivePowerSetpoint(), REACTIVE_POWER_SET_POINT, 1, TypedValue.TRACE_SEVERITY)); } } } + if (!reports.isEmpty()) { + Reporter vscConverterStationsReporter = subReporter.createSubReporter(VSC_CONVERTER_STATIONS_KEY, VSC_CONVERTER_STATIONS_NAME); + reports.forEach(vscConverterStationsReporter::report); + } if (modificationsCount > 0) { subReporter.report(new Report("vscModificationsResume", "${count} vsc converter station(s) have been modified.", Map.of( - "count", new TypedValue(modificationsCount, TypedValue.UNTYPED), - Report.REPORT_SEVERITY_KEY, TypedValue.INFO_SEVERITY + "count", new TypedValue(modificationsCount, TypedValue.UNTYPED), + Report.REPORT_SEVERITY_KEY, TypedValue.INFO_SEVERITY ))); } } private void applyShuntCompensatorModification(Network network, Reporter subReporter) { int modificationsCount = 0; + List reports = new ArrayList<>(); for (VoltageInitShuntCompensatorModificationInfos m : voltageInitModificationInfos.getShuntCompensators()) { final ShuntCompensator shuntCompensator = network.getShuntCompensator(m.getShuntCompensatorId()); if (shuntCompensator == null) { - Reporter reporter = subReporter.createSubReporter(SHUNT_COMPENSATOR_KEY, SHUNT_COMPENSATOR_NAME, "compensatorId", m.getShuntCompensatorId()); - reporter.report(Report.builder().withKey("shuntCompensatorNotFound") - .withDefaultMessage("Shunt compensator with id=${id} not found") - .withValue("id", m.getShuntCompensatorId()) - .withSeverity(TypedValue.WARN_SEVERITY).build()); + reports.add(Report.builder().withKey("shuntCompensatorNotFound") + .withDefaultMessage("Shunt compensator with id=${id} not found") + .withValue("id", m.getShuntCompensatorId()) + .withSeverity(TypedValue.WARN_SEVERITY).build()); } else if (m.getSectionCount() != null || m.getConnect() != null) { - List reports = new ArrayList<>(); + 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) { - reports.add(Report.builder().withKey("shuntCompensatorSectionCountUndefined") - .withDefaultMessage("\tSection count value is undefined") - .withSeverity(TypedValue.WARN_SEVERITY).build()); + reportsShunt.add(Report.builder().withKey("shuntCompensatorSectionCountUndefined") + .withDefaultMessage("\tSection count value is undefined") + .withSeverity(TypedValue.WARN_SEVERITY).build()); } else { if (m.getSectionCount() == 0) { shuntCompensatorTerminal.disconnect(); - reports.add(Report.builder().withKey("shuntCompensatorDisconnected") - .withDefaultMessage("\tShunt compensator disconnected") - .withSeverity(TypedValue.TRACE_SEVERITY).build()); + reportsShunt.add(Report.builder().withKey("shuntCompensatorDisconnected") + .withDefaultMessage("\tShunt compensator disconnected") + .withSeverity(TypedValue.TRACE_SEVERITY).build()); } if (m.getSectionCount() != currentSectionCount) { shuntCompensator.setSectionCount(m.getSectionCount()); - reports.add(ModificationUtils.buildModificationReport(currentSectionCount, m.getSectionCount(), SECTION_COUNT, 1, TypedValue.TRACE_SEVERITY)); + reportsShunt.add(ModificationUtils.buildModificationReport(currentSectionCount, m.getSectionCount(), SECTION_COUNT, 1, TypedValue.TRACE_SEVERITY)); } } } else { // shunt compensator is disconnected if (m.getConnect() == null) { - reports.add(Report.builder().withKey("shuntCompensatorConnectUndefined") - .withDefaultMessage("\tConnect value is undefined") - .withSeverity(TypedValue.WARN_SEVERITY).build()); + reportsShunt.add(Report.builder().withKey("shuntCompensatorConnectUndefined") + .withDefaultMessage("\tConnect value is undefined") + .withSeverity(TypedValue.WARN_SEVERITY).build()); } else { if (Boolean.TRUE.equals(m.getConnect())) { shuntCompensatorTerminal.connect(); - reports.add(Report.builder().withKey("shuntCompensatorReconnected") - .withDefaultMessage("\tShunt compensator reconnected") - .withSeverity(TypedValue.TRACE_SEVERITY).build()); + reportsShunt.add(Report.builder().withKey("shuntCompensatorReconnected") + .withDefaultMessage("\tShunt compensator reconnected") + .withSeverity(TypedValue.TRACE_SEVERITY).build()); } if (m.getSectionCount() != currentSectionCount) { shuntCompensator.setSectionCount(m.getSectionCount()); - reports.add(ModificationUtils.buildModificationReport(currentSectionCount, m.getSectionCount(), SECTION_COUNT, 1, TypedValue.TRACE_SEVERITY)); + reportsShunt.add(ModificationUtils.buildModificationReport(currentSectionCount, m.getSectionCount(), SECTION_COUNT, 1, TypedValue.TRACE_SEVERITY)); } } } - if (!reports.isEmpty()) { + if (!reportsShunt.isEmpty()) { modificationsCount++; - Reporter reporter = subReporter.createSubReporter(SHUNT_COMPENSATOR_KEY, SHUNT_COMPENSATOR_NAME, "compensatorId", m.getShuntCompensatorId()); - reporter.report(Report.builder().withKey("shuntCompensatorModification") - .withDefaultMessage("Shunt compensator with id=${id} modified :") - .withValue("id", m.getShuntCompensatorId()) - .withSeverity(TypedValue.TRACE_SEVERITY).build()); - reports.forEach(reporter::report); + reports.add(Report.builder().withKey("shuntCompensatorModification") + .withDefaultMessage("Shunt compensator with id=${id} modified :") + .withValue("id", m.getShuntCompensatorId()) + .withSeverity(TypedValue.TRACE_SEVERITY).build()); + reportsShunt.forEach(reports::add); } } } + if (!reports.isEmpty()) { + Reporter shuntCompensatorsReporter = subReporter.createSubReporter(SHUNT_COMPENSATORS_KEY, SHUNT_COMPENSATORS_NAME); + reports.forEach(shuntCompensatorsReporter::report); + } if (modificationsCount > 0) { subReporter.report(new Report("shuntCompensatorModificationsResume", "${count} shunt compensator(s) have been modified.", Map.of( - "count", new TypedValue(modificationsCount, TypedValue.UNTYPED), - Report.REPORT_SEVERITY_KEY, TypedValue.INFO_SEVERITY + "count", new TypedValue(modificationsCount, TypedValue.UNTYPED), + Report.REPORT_SEVERITY_KEY, TypedValue.INFO_SEVERITY ))); } } diff --git a/src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java index 81c010149..e08660a8b 100644 --- a/src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java +++ b/src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java @@ -70,6 +70,23 @@ public void specificSetUp() { createEquipments(); } + @Test + public void testByModificationError() throws Exception { + // Test with empty list of formulas + checkCreationApplicationStatus(ByFormulaModificationInfos.builder().identifiableType(getIdentifiableType()).formulaInfosList(List.of()).build(), + NetworkModificationResult.ApplicationStatus.WITH_ERRORS); + + // Test with empty list of filters in formula + FormulaInfos formulaInfos = FormulaInfos.builder() + .fieldOrValue1(ReferenceFieldOrValue.builder().value(50.).build()) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(50.).build()) + .operator(Operator.ADDITION) + .filters(List.of()) + .build(); + checkCreationApplicationStatus(ByFormulaModificationInfos.builder().identifiableType(getIdentifiableType()).formulaInfosList(List.of(formulaInfos)).build(), + NetworkModificationResult.ApplicationStatus.WITH_ERRORS); + } + protected void checkCreateWithWarning(List formulaInfos, List existingEquipmentList) throws Exception { FilterEquipments filter = getFilterEquipments(FILTER_WITH_ONE_WRONG_ID, "filterWithWrongId", existingEquipmentList, List.of("wrongId")); @@ -135,7 +152,7 @@ public void testCopy() throws Exception { wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), filters.stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false); } - private void checkCreationApplicationStatus(ByFormulaModificationInfos byFormulaModificationInfos, + protected void checkCreationApplicationStatus(ByFormulaModificationInfos byFormulaModificationInfos, NetworkModificationResult.ApplicationStatus applicationStatus) throws Exception { String modificationToCreateJson = mapper.writeValueAsString(byFormulaModificationInfos); @@ -201,11 +218,11 @@ protected FormulaInfos getFormulaInfo(String editedField, .build(); } - private Map handleQueryParams(UUID networkUuid, List filterIds) { + Map handleQueryParams(UUID networkUuid, List filterIds) { return Map.of("networkUuid", WireMock.equalTo(String.valueOf(networkUuid)), "variantId", WireMock.equalTo("variant_1"), "ids", WireMock.matching(filterIds.stream().map(uuid -> ".+").collect(Collectors.joining(",")))); } - private String getPath(UUID networkUuid, boolean isRegexPhat) { + String getPath(UUID networkUuid, boolean isRegexPhat) { if (isRegexPhat) { return "/v1/filters/export\\?networkUuid=" + networkUuid + "\\&variantId=variant_1\\&ids="; } diff --git a/src/test/java/org/gridsuite/modification/server/modifications/BatteryModificationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/BatteryModificationTest.java index d1319ea0f..8c9b2dea9 100644 --- a/src/test/java/org/gridsuite/modification/server/modifications/BatteryModificationTest.java +++ b/src/test/java/org/gridsuite/modification/server/modifications/BatteryModificationTest.java @@ -16,6 +16,7 @@ import org.junit.Test; import org.junit.jupiter.api.Tag; import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; import org.springframework.util.CollectionUtils; import java.util.*; @@ -217,6 +218,61 @@ public void testDroopUnchanged() throws Exception { assertEquals(18f, createdModification.getDroop().getValue()); } + @Test + public 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()); + assertEquals(true, 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 + public void testActivePowerZeroOrBetweenMinAndMaxActivePower() throws Exception { + BatteryModificationInfos batteryModificationInfos = (BatteryModificationInfos) buildModification(); + Battery battery = getNetwork().getBattery("v3Battery"); + battery.setTargetP(80.) + .setMinP(0.) + .setMaxP(100.); + batteryModificationInfos.setActivePowerSetpoint(new AttributeModification<>(155.0, OperationType.SET)); + + Double minActivePower = batteryModificationInfos.getMinActivePower() != null ? batteryModificationInfos.getMinActivePower().getValue() : battery.getMinP(); + Double maxActivePower = batteryModificationInfos.getMaxActivePower() != null ? batteryModificationInfos.getMaxActivePower().getValue() : battery.getMaxP(); + Double activePower = batteryModificationInfos.getActivePowerSetpoint() != null ? batteryModificationInfos.getActivePowerSetpoint().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 public void testMinQGreaterThanMaxQ() throws Exception { BatteryModificationInfos batteryModificationInfos = (BatteryModificationInfos) buildModification(); diff --git a/src/test/java/org/gridsuite/modification/server/modifications/GenerationDispatchTest.java b/src/test/java/org/gridsuite/modification/server/modifications/GenerationDispatchTest.java index 84b7184ab..865804c5f 100644 --- a/src/test/java/org/gridsuite/modification/server/modifications/GenerationDispatchTest.java +++ b/src/test/java/org/gridsuite/modification/server/modifications/GenerationDispatchTest.java @@ -23,10 +23,9 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MvcResult; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.*; import java.util.stream.Collectors; import static org.gridsuite.modification.server.utils.TestUtils.assertLogMessage; @@ -34,6 +33,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -47,6 +47,9 @@ public class GenerationDispatchTest extends AbstractNetworkModificationTest { private static final String GH3_ID = "GH3"; private static final String GTH1_ID = "GTH1"; private static final String GTH2_ID = "GTH2"; + private static final String BATTERY1_ID = "BATTERY1"; + private static final String BATTERY2_ID = "BATTERY2"; + private static final String BATTERY3_ID = "BATTERY3"; private static final String TEST1_ID = "TEST1"; private static final String GROUP1_ID = "GROUP1"; private static final String GROUP2_ID = "GROUP2"; @@ -90,11 +93,34 @@ private FilterEquipments getFilterEquipments(UUID filterID, String filterName, .build(); } + private void assertLogReportsForDefaultNetwork(double batteryBalanceOnSc2) { + int firstSynchronousComponentNum = getNetwork().getGenerator(GTH1_ID).getTerminal().getBusView().getBus().getSynchronousComponent().getNum(); // GTH1 is in first synchronous component + assertLogMessage("The total demand is : 528.0 MW", "TotalDemand" + firstSynchronousComponentNum, reportService); + assertLogMessage("The total amount of fixed supply is : 0.0 MW", "TotalAmountFixedSupply" + firstSynchronousComponentNum, reportService); + assertLogMessage("The HVDC balance is : 90.0 MW", "TotalOutwardHvdcFlow" + firstSynchronousComponentNum, reportService); + assertLogMessage("The battery balance is : 0.0 MW", "TotalActiveBatteryTargetP" + firstSynchronousComponentNum, reportService); + assertLogMessage("The total amount of supply to be dispatched is : 438.0 MW", "TotalAmountSupplyToBeDispatched" + firstSynchronousComponentNum, reportService); + assertLogMessage("The supply-demand balance could not be met : the remaining power imbalance is 138.0 MW", "SupplyDemandBalanceCouldNotBeMet" + firstSynchronousComponentNum, reportService); + // on SC 2, we have to substract the battery balance + final double defaultTotalAmount = 330.0; + DecimalFormat df = new DecimalFormat("#0.0", new DecimalFormatSymbols(Locale.US)); + final String totalAmount = df.format(defaultTotalAmount - batteryBalanceOnSc2); + int secondSynchronousComponentNum = getNetwork().getGenerator(GH1_ID).getTerminal().getBusView().getBus().getSynchronousComponent().getNum(); // GH1 is in second synchronous component + assertLogMessage("The total demand is : 240.0 MW", "TotalDemand" + secondSynchronousComponentNum, reportService); + assertLogMessage("The total amount of fixed supply is : 0.0 MW", "TotalAmountFixedSupply" + secondSynchronousComponentNum, reportService); + assertLogMessage("The HVDC balance is : -90.0 MW", "TotalOutwardHvdcFlow" + secondSynchronousComponentNum, reportService); + assertLogMessage("The battery balance is : " + df.format(batteryBalanceOnSc2) + " MW", "TotalActiveBatteryTargetP" + secondSynchronousComponentNum, reportService); + assertLogMessage("The total amount of supply to be dispatched is : " + totalAmount + " MW", "TotalAmountSupplyToBeDispatched" + secondSynchronousComponentNum, reportService); + assertLogMessage("Marginal cost: 150.0", "MaxUsedMarginalCost" + secondSynchronousComponentNum, reportService); + assertLogMessage("The supply-demand balance could be met", "SupplyDemandBalanceCouldBeMet" + secondSynchronousComponentNum, reportService); + assertLogMessage("Sum of generator active power setpoints in SOUTH region: " + totalAmount + " MW (NUCLEAR: 0.0 MW, THERMAL: 0.0 MW, HYDRO: " + totalAmount + " MW, WIND AND SOLAR: 0.0 MW, OTHER: 0.0 MW).", "SumGeneratorActivePowerSOUTH" + secondSynchronousComponentNum, reportService); + } + @Test public void testGenerationDispatch() throws Exception { ModificationInfos modification = buildModification(); - // network with 2 synchronous components, 2 hvdc lines between them and no forcedOutageRate and plannedOutageRate for the generators + // network with 2 synchronous components, no battery, 2 hvdc lines between them and no forcedOutageRate and plannedOutageRate for the generators setNetwork(Network.read("testGenerationDispatch.xiidm", getClass().getResourceAsStream("/testGenerationDispatch.xiidm"))); String modificationJson = mapper.writeValueAsString(modification); @@ -103,22 +129,67 @@ public void testGenerationDispatch() throws Exception { assertNetworkAfterCreationWithStandardLossCoefficient(); - // test total demand and remaining power imbalance on synchronous components - int firstSynchronousComponentNum = getNetwork().getGenerator(GTH1_ID).getTerminal().getBusView().getBus().getSynchronousComponent().getNum(); // GTH1 is in first synchronous component - assertLogMessage("The total demand is : 528.0 MW", "TotalDemand" + firstSynchronousComponentNum, reportService); - assertLogMessage("The total amount of fixed supply is : 0.0 MW", "TotalAmountFixedSupply" + firstSynchronousComponentNum, reportService); - assertLogMessage("The HVDC balance is : 90.0 MW", "TotalOutwardHvdcFlow" + firstSynchronousComponentNum, reportService); - assertLogMessage("The total amount of supply to be dispatched is : 438.0 MW", "TotalAmountSupplyToBeDispatched" + firstSynchronousComponentNum, reportService); - assertLogMessage("The supply-demand balance could not be met : the remaining power imbalance is 138.0 MW", "SupplyDemandBalanceCouldNotBeMet" + firstSynchronousComponentNum, reportService); + assertLogReportsForDefaultNetwork(0.); + } - int secondSynchronousComponentNum = getNetwork().getGenerator(GH1_ID).getTerminal().getBusView().getBus().getSynchronousComponent().getNum(); // GH1 is in second synchronous component - assertLogMessage("The total demand is : 240.0 MW", "TotalDemand" + secondSynchronousComponentNum, reportService); - assertLogMessage("The total amount of fixed supply is : 0.0 MW", "TotalAmountFixedSupply" + secondSynchronousComponentNum, reportService); - assertLogMessage("The HVDC balance is : -90.0 MW", "TotalOutwardHvdcFlow" + secondSynchronousComponentNum, reportService); - assertLogMessage("The total amount of supply to be dispatched is : 330.0 MW", "TotalAmountSupplyToBeDispatched" + secondSynchronousComponentNum, reportService); - assertLogMessage("Marginal cost: 150.0", "MaxUsedMarginalCost" + secondSynchronousComponentNum, reportService); - assertLogMessage("The supply-demand balance could be met", "SupplyDemandBalanceCouldBeMet" + secondSynchronousComponentNum, reportService); - assertLogMessage("Sum of generator active power setpoints in SOUTH region: 330.0 MW.", "SumGeneratorActivePowerSOUTH" + secondSynchronousComponentNum, reportService); + @Test + public void testGenerationDispatchWithBattery() throws Exception { + ModificationInfos modification = buildModification(); + + // same than testGenerationDispatch, with 3 Batteries (in 2nd SC) + setNetwork(Network.read("testGenerationDispatchWithBatteries.xiidm", getClass().getResourceAsStream("/testGenerationDispatchWithBatteries.xiidm"))); + // only 2 are connected + assertTrue(getNetwork().getBattery(BATTERY1_ID).getTerminal().isConnected()); + assertTrue(getNetwork().getBattery(BATTERY2_ID).getTerminal().isConnected()); + assertFalse(getNetwork().getBattery(BATTERY3_ID).getTerminal().isConnected()); + final double batteryTotalTargetP = getNetwork().getBattery(BATTERY1_ID).getTargetP() + getNetwork().getBattery(BATTERY2_ID).getTargetP(); + + String modificationJson = mapper.writeValueAsString(modification); + mockMvc.perform(post(getNetworkModificationUri()).content(modificationJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + + assertLogReportsForDefaultNetwork(batteryTotalTargetP); + } + + @Test + public void testGenerationDispatchWithBatteryConnection() throws Exception { + ModificationInfos modification = buildModification(); + + // network with 3 Batteries (in 2nd SC) + setNetwork(Network.read("testGenerationDispatch.xiidm", getClass().getResourceAsStream("/testGenerationDispatchWithBatteries.xiidm"))); + // connect the 3rd one + assertTrue(getNetwork().getBattery(BATTERY1_ID).getTerminal().isConnected()); + assertTrue(getNetwork().getBattery(BATTERY2_ID).getTerminal().isConnected()); + assertFalse(getNetwork().getBattery(BATTERY3_ID).getTerminal().isConnected()); + assertTrue(getNetwork().getBattery(BATTERY3_ID).getTargetP() > 0); + getNetwork().getBattery(BATTERY3_ID).getTerminal().connect(); + final double batteryTotalTargetP = getNetwork().getBattery(BATTERY1_ID).getTargetP() + getNetwork().getBattery(BATTERY2_ID).getTargetP() + getNetwork().getBattery(BATTERY3_ID).getTargetP(); + + String modificationJson = mapper.writeValueAsString(modification); + mockMvc.perform(post(getNetworkModificationUri()).content(modificationJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + + assertLogReportsForDefaultNetwork(batteryTotalTargetP); + } + + @Test + public void testGenerationDispatchWithMultipleEnergySource() throws Exception { + ModificationInfos modification = buildModification(); + + setNetwork(Network.read("testGenerationDispatchWithMultipleEnergySource.xiidm", getClass().getResourceAsStream("/testGenerationDispatchWithMultipleEnergySource.xiidm"))); + + String modificationJson = mapper.writeValueAsString(modification); + mockMvc.perform(post(getNetworkModificationUri()).content(modificationJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + + int synchronousComponentNum = getNetwork().getGenerator(GH1_ID).getTerminal().getBusView().getBus().getSynchronousComponent().getNum(); + assertLogMessage("The total demand is : 768.0 MW", "TotalDemand" + synchronousComponentNum, reportService); + assertLogMessage("The total amount of fixed supply is : 0.0 MW", "TotalAmountFixedSupply" + synchronousComponentNum, reportService); + assertLogMessage("The HVDC balance is : -90.0 MW", "TotalOutwardHvdcFlow" + synchronousComponentNum, reportService); + assertLogMessage("The total amount of supply to be dispatched is : 858.0 MW", "TotalAmountSupplyToBeDispatched" + synchronousComponentNum, reportService); + assertLogMessage("Marginal cost: 28.0", "MaxUsedMarginalCost" + synchronousComponentNum, reportService); + assertLogMessage("The supply-demand balance could be met", "SupplyDemandBalanceCouldBeMet" + synchronousComponentNum, reportService); + assertLogMessage("Sum of generator active power setpoints in SOUTH region: 858.0 MW (NUCLEAR: 150.0 MW, THERMAL: 200.0 MW, HYDRO: 108.0 MW, WIND AND SOLAR: 150.0 MW, OTHER: 250.0 MW).", "SumGeneratorActivePowerSOUTH" + synchronousComponentNum, reportService); } @Test @@ -248,7 +319,7 @@ public void testGenerationDispatchWithMaxPReduction() throws Exception { assertLogMessage("The total amount of supply to be dispatched is : 330.0 MW", "TotalAmountSupplyToBeDispatched" + secondSynchronousComponentNum, reportService); assertLogMessage("Marginal cost: 150.0", "MaxUsedMarginalCost" + secondSynchronousComponentNum, reportService); assertLogMessage("The supply-demand balance could be met", "SupplyDemandBalanceCouldBeMet" + secondSynchronousComponentNum, reportService); - assertLogMessage("Sum of generator active power setpoints in WEST region: 330.0 MW.", "SumGeneratorActivePowerWEST" + secondSynchronousComponentNum, reportService); + assertLogMessage("Sum of generator active power setpoints in WEST region: 330.0 MW (NUCLEAR: 0.0 MW, THERMAL: 0.0 MW, HYDRO: 330.0 MW, WIND AND SOLAR: 0.0 MW, OTHER: 0.0 MW).", "SumGeneratorActivePowerWEST" + secondSynchronousComponentNum, reportService); wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), filters.stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false); } @@ -313,7 +384,7 @@ public void testGenerationDispatchGeneratorsWithFixedSupply() throws Exception { assertLogMessage("The total amount of supply to be dispatched is : 330.0 MW", "TotalAmountSupplyToBeDispatched" + secondSynchronousComponentNum, reportService); assertLogMessage("Marginal cost: 150.0", "MaxUsedMarginalCost" + secondSynchronousComponentNum, reportService); assertLogMessage("The supply-demand balance could be met", "SupplyDemandBalanceCouldBeMet" + secondSynchronousComponentNum, reportService); - assertLogMessage("Sum of generator active power setpoints in EAST region: 330.0 MW.", "SumGeneratorActivePowerEAST" + secondSynchronousComponentNum, reportService); + assertLogMessage("Sum of generator active power setpoints in EAST region: 330.0 MW (NUCLEAR: 0.0 MW, THERMAL: 0.0 MW, HYDRO: 330.0 MW, WIND AND SOLAR: 0.0 MW, OTHER: 0.0 MW).", "SumGeneratorActivePowerEAST" + secondSynchronousComponentNum, reportService); wireMockUtils.verifyGetRequest(stubIdForPmaxReduction, PATH, handleQueryParams(getNetworkUuid(), filtersForPmaxReduction.stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false); wireMockUtils.verifyGetRequest(stubIdForFixedSupply, PATH, handleQueryParams(getNetworkUuid(), filtersForFixedSupply.stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false); @@ -396,7 +467,7 @@ public void testGenerationDispatchWithFrequencyReserve() throws Exception { assertLogMessage("The total amount of fixed supply is : 0.0 MW", "TotalAmountFixedSupply" + firstSynchronousComponentNum, reportService); assertLogMessage("The HVDC balance is : 90.0 MW", "TotalOutwardHvdcFlow" + firstSynchronousComponentNum, reportService); assertLogMessage("The total amount of supply to be dispatched is : 438.0 MW", "TotalAmountSupplyToBeDispatched" + firstSynchronousComponentNum, reportService); - assertLogMessage("The supply-demand balance could not be met : the remaining power imbalance is 177.92000000000002 MW", "SupplyDemandBalanceCouldNotBeMet" + firstSynchronousComponentNum, reportService); + assertLogMessage("The supply-demand balance could not be met : the remaining power imbalance is 177.9 MW", "SupplyDemandBalanceCouldNotBeMet" + firstSynchronousComponentNum, reportService); int secondSynchronousComponentNum = getNetwork().getGenerator(GH1_ID).getTerminal().getBusView().getBus().getSynchronousComponent().getNum(); // GH1 is in second synchronous component assertLogMessage("The total demand is : 240.0 MW", "TotalDemand" + secondSynchronousComponentNum, reportService); @@ -405,7 +476,7 @@ public void testGenerationDispatchWithFrequencyReserve() throws Exception { assertLogMessage("The total amount of supply to be dispatched is : 330.0 MW", "TotalAmountSupplyToBeDispatched" + secondSynchronousComponentNum, reportService); assertLogMessage("Marginal cost: 150.0", "MaxUsedMarginalCost" + secondSynchronousComponentNum, reportService); assertLogMessage("The supply-demand balance could be met", "SupplyDemandBalanceCouldBeMet" + secondSynchronousComponentNum, reportService); - assertLogMessage("Sum of generator active power setpoints in WEST region: 330.0 MW.", "SumGeneratorActivePowerWEST" + secondSynchronousComponentNum, reportService); + assertLogMessage("Sum of generator active power setpoints in WEST region: 330.0 MW (NUCLEAR: 0.0 MW, THERMAL: 0.0 MW, HYDRO: 330.0 MW, WIND AND SOLAR: 0.0 MW, OTHER: 0.0 MW).", "SumGeneratorActivePowerWEST" + secondSynchronousComponentNum, reportService); wireMockUtils.verifyGetRequest(stubIdForPmaxReduction, PATH, handleQueryParams(getNetworkUuid(), getGeneratorsWithoutOutageFilters123().stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false); wireMockUtils.verifyGetRequest(stubIdForFrequencyReserve1, PATH, handleQueryParams(getNetworkUuid(), getGeneratorsFrequencyReserveFilters45().stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false); @@ -544,10 +615,10 @@ public void testGenerationDispatchWithMaxValueLessThanMinP() throws Exception { assertLogMessage("The total amount of fixed supply is : 0.0 MW", "TotalAmountFixedSupply" + firstSynchronousComponentNum, reportService); assertLogMessage("The HVDC balance is : 90.0 MW", "TotalOutwardHvdcFlow" + firstSynchronousComponentNum, reportService); assertLogMessage("The total amount of supply to be dispatched is : 438.0 MW", "TotalAmountSupplyToBeDispatched" + firstSynchronousComponentNum, reportService); - assertLogNthMessage("The active power set point of generator TEST1 has been set to 40.375 MW", "GeneratorSetTargetP" + firstSynchronousComponentNum, reportService, 1); + assertLogNthMessage("The active power set point of generator TEST1 has been set to 40.4 MW", "GeneratorSetTargetP" + firstSynchronousComponentNum, reportService, 1); assertLogNthMessage("The active power set point of generator GTH1 has been set to 80.0 MW", "GeneratorSetTargetP" + firstSynchronousComponentNum, reportService, 2); assertLogNthMessage("The active power set point of generator GTH2 has been set to 146.0 MW", "GeneratorSetTargetP" + firstSynchronousComponentNum, reportService, 3); - assertLogMessage("The supply-demand balance could not be met : the remaining power imbalance is 171.625 MW", "SupplyDemandBalanceCouldNotBeMet" + firstSynchronousComponentNum, reportService); + assertLogMessage("The supply-demand balance could not be met : the remaining power imbalance is 171.6 MW", "SupplyDemandBalanceCouldNotBeMet" + firstSynchronousComponentNum, reportService); int secondSynchronousComponentNum = getNetwork().getGenerator(GH1_ID).getTerminal().getBusView().getBus().getSynchronousComponent().getNum(); // GH1 is in second synchronous component assertLogMessage("The total demand is : 240.0 MW", "TotalDemand" + secondSynchronousComponentNum, reportService); assertLogMessage("The total amount of fixed supply is : 0.0 MW", "TotalAmountFixedSupply" + secondSynchronousComponentNum, reportService); @@ -556,10 +627,10 @@ public void testGenerationDispatchWithMaxValueLessThanMinP() throws Exception { assertLogNthMessage("The active power set point of generator GH1 has been set to 80.0 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 1); assertLogNthMessage("The active power set point of generator GH2 has been set to 60.0 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 2); assertLogNthMessage("The active power set point of generator GH3 has been set to 126.1 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 3); - assertLogNthMessage("The active power set point of generator ABC has been set to 63.900000000000006 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 4); + assertLogNthMessage("The active power set point of generator ABC has been set to 63.9 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 4); assertLogMessage("Marginal cost: 150.0", "MaxUsedMarginalCost" + secondSynchronousComponentNum, reportService); assertLogMessage("The supply-demand balance could be met", "SupplyDemandBalanceCouldBeMet" + secondSynchronousComponentNum, reportService); - assertLogMessage("Sum of generator active power setpoints in NORTH region: 330.0 MW.", "SumGeneratorActivePowerNORTH" + secondSynchronousComponentNum, reportService); + assertLogMessage("Sum of generator active power setpoints in NORTH region: 330.0 MW (NUCLEAR: 0.0 MW, THERMAL: 0.0 MW, HYDRO: 330.0 MW, WIND AND SOLAR: 0.0 MW, OTHER: 0.0 MW).", "SumGeneratorActivePowerNORTH" + secondSynchronousComponentNum, reportService); wireMockUtils.verifyGetRequest(stubIdForPmaxReduction, PATH, handleQueryParams(getNetworkUuid(), getGeneratorsWithoutOutageFilters123().stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false); wireMockUtils.verifyGetRequest(stubIdForFrequencyReserve1, PATH, handleQueryParams(getNetworkUuid(), getGeneratorsFrequencyReserveFilters45().stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false); diff --git a/src/test/java/org/gridsuite/modification/server/modifications/GeneratorModificationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/GeneratorModificationTest.java index 6b753fdc1..32eed149c 100644 --- a/src/test/java/org/gridsuite/modification/server/modifications/GeneratorModificationTest.java +++ b/src/test/java/org/gridsuite/modification/server/modifications/GeneratorModificationTest.java @@ -391,6 +391,28 @@ public void testMinQGreaterThanMaxQ() throws Exception { generatorModificationInfos.getErrorType().name(), reportService); } + @Test + public void testActivePowerZeroOrBetweenMinAndMaxActivePower() throws Exception { + GeneratorModificationInfos generatorModificationInfos = (GeneratorModificationInfos) buildModification(); + Generator generator = getNetwork().getGenerator("idGenerator"); + generator.setTargetP(80.) + .setMinP(10.) + .setMaxP(150.); + + generatorModificationInfos.setActivePowerSetpoint(new AttributeModification<>(110.0, OperationType.SET)); + + Double minActivePower = generatorModificationInfos.getMinActivePower() != null ? generatorModificationInfos.getMinActivePower().getValue() : generator.getMinP(); + Double maxActivePower = generatorModificationInfos.getMaxActivePower() != null ? generatorModificationInfos.getMaxActivePower().getValue() : generator.getMaxP(); + Double activePower = generatorModificationInfos.getActivePowerSetpoint() != null ? generatorModificationInfos.getActivePowerSetpoint().getValue() : generator.getTargetP(); + + String modificationToCreateJson = mapper.writeValueAsString(generatorModificationInfos); + mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn(); + assertLogMessage("MODIFY_GENERATOR_ERROR : Generator '" + "idGenerator" + "' : Active power " + activePower + " is expected to be equal to 0 or within the range of minimum active power and maximum active power: [" + minActivePower + ", " + maxActivePower + "]", + generatorModificationInfos.getErrorType().name(), reportService); + + } + @Test public void testUnsetAttributes() throws Exception { GeneratorModificationInfos generatorModificationInfos = (GeneratorModificationInfos) buildModification(); diff --git a/src/test/java/org/gridsuite/modification/server/modifications/TwoWindingsTransformerByFormulaModificationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/TwoWindingsTransformerByFormulaModificationTest.java new file mode 100644 index 000000000..b3d78c507 --- /dev/null +++ b/src/test/java/org/gridsuite/modification/server/modifications/TwoWindingsTransformerByFormulaModificationTest.java @@ -0,0 +1,576 @@ +package org.gridsuite.modification.server.modifications; + +import com.github.tomakehurst.wiremock.client.WireMock; +import com.powsybl.iidm.network.IdentifiableType; +import com.powsybl.iidm.network.PhaseTapChanger; +import com.powsybl.iidm.network.PhaseTapChangerAdder; +import com.powsybl.iidm.network.RatioTapChanger; +import com.powsybl.iidm.network.RatioTapChangerAdder; +import com.powsybl.iidm.network.Substation; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import org.gridsuite.modification.server.dto.ByFormulaModificationInfos; +import org.gridsuite.modification.server.dto.FilterEquipments; +import org.gridsuite.modification.server.dto.IdentifiableAttributes; +import org.gridsuite.modification.server.dto.NetworkModificationResult; +import org.gridsuite.modification.server.dto.formula.FormulaInfos; +import org.gridsuite.modification.server.dto.formula.Operator; +import org.gridsuite.modification.server.dto.formula.ReferenceFieldOrValue; +import org.gridsuite.modification.server.dto.formula.equipmentfield.TwoWindingsTransformerField; +import org.junit.Test; + +import java.util.List; +import java.util.UUID; + +import static org.gridsuite.modification.server.utils.NetworkUtil.createTwoWindingsTransformer; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +public class TwoWindingsTransformerByFormulaModificationTest extends AbstractByFormulaModificationTest { + private static final String TWT_ID_1 = "twt1"; + private static final String TWT_ID_2 = "twt2"; + private static final String TWT_ID_3 = "twt3"; + private static final String TWT_ID_4 = "twt4"; + private static final String TWT_ID_5 = "twt5"; + private static final String TWT_ID_6 = "twt6"; + + @Test + public void testModifyTwtWithError() throws Exception { + IdentifiableAttributes identifiableAttributes1 = getIdentifiableAttributes(TWT_ID_4, 1.); + IdentifiableAttributes identifiableAttributes2 = getIdentifiableAttributes(TWT_ID_6, 1.); + FilterEquipments filter = getFilterEquipments(FILTER_ID_4, "filter4", List.of(identifiableAttributes1, identifiableAttributes2), List.of()); + + UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/filters/export\\?networkUuid=" + getNetworkUuid() + "&variantId=variant_1&ids=" + FILTER_ID_4)) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(List.of(filter))) + .withHeader("Content-Type", "application/json"))).getId(); + FormulaInfos formulaInfos = FormulaInfos.builder() + .filters(List.of(filter4)) + .fieldOrValue2(ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.RATIO_TAP_POSITION.name()).build()) + .fieldOrValue1(ReferenceFieldOrValue.builder().value(1.).build()) + .editedField(TwoWindingsTransformerField.RATIO_TAP_POSITION.name()) + .operator(Operator.ADDITION) + .build(); + checkCreationApplicationStatus(ByFormulaModificationInfos.builder() + .identifiableType(getIdentifiableType()) + .formulaInfosList(List.of(formulaInfos)) + .build(), + NetworkModificationResult.ApplicationStatus.WITH_ERRORS); + + assertNull(getNetwork().getTwoWindingsTransformer(TWT_ID_4).getRatioTapChanger()); + assertNull(getNetwork().getTwoWindingsTransformer(TWT_ID_6).getRatioTapChanger()); + + wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), List.of(FILTER_ID_4)), false); + } + + @Test + public void testDivisionByZero() throws Exception { + IdentifiableAttributes identifiableAttributes1 = getIdentifiableAttributes(TWT_ID_4, 1.); + IdentifiableAttributes identifiableAttributes2 = getIdentifiableAttributes(TWT_ID_6, 1.); + FilterEquipments filter = getFilterEquipments(FILTER_ID_4, "filter4", List.of(identifiableAttributes1, identifiableAttributes2), List.of()); + + UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/filters/export\\?networkUuid=" + getNetworkUuid() + "&variantId=variant_1&ids=" + FILTER_ID_4)) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(List.of(filter))) + .withHeader("Content-Type", "application/json"))).getId(); + + // Test division by 0 + FormulaInfos formulaInfos2 = FormulaInfos.builder() + .fieldOrValue1(ReferenceFieldOrValue.builder().value(50.).build()) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(0.).build()) + .operator(Operator.DIVISION) + .filters(List.of(filter4)) + .build(); + + checkCreationApplicationStatus(ByFormulaModificationInfos.builder().identifiableType(getIdentifiableType()).formulaInfosList(List.of(formulaInfos2)).build(), + NetworkModificationResult.ApplicationStatus.WITH_ERRORS); + + wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), List.of(FILTER_ID_4)), false); + } + + @Test + public void testModifyTwtWithWarning() throws Exception { + IdentifiableAttributes identifiableAttributes1 = getIdentifiableAttributes(TWT_ID_1, 1.); + IdentifiableAttributes identifiableAttributes2 = getIdentifiableAttributes(TWT_ID_2, 1.); + IdentifiableAttributes identifiableAttributes3 = getIdentifiableAttributes(TWT_ID_4, 1.); + IdentifiableAttributes identifiableAttributes4 = getIdentifiableAttributes(TWT_ID_6, 1.); + FilterEquipments filterTwt1 = getFilterEquipments(FILTER_ID_1, "filter1", List.of(identifiableAttributes1, identifiableAttributes2), List.of()); + FilterEquipments filterTwt2 = getFilterEquipments(FILTER_ID_4, "filter4", List.of(identifiableAttributes3, identifiableAttributes4), List.of()); + + UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching(getPath(getNetworkUuid(), true) + ".{2,}")) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(List.of(filterTwt1, filterTwt2))) + .withHeader("Content-Type", "application/json"))).getId(); + + FormulaInfos formulaInfos = FormulaInfos.builder() + .filters(List.of(filter1, filter4)) + .fieldOrValue2(ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.RATIO_TAP_POSITION.name()).build()) + .fieldOrValue1(ReferenceFieldOrValue.builder().value(1.).build()) + .editedField(TwoWindingsTransformerField.RATIO_TAP_POSITION.name()) + .operator(Operator.ADDITION) + .build(); + + checkCreationApplicationStatus(ByFormulaModificationInfos.builder() + .identifiableType(getIdentifiableType()) + .formulaInfosList(List.of(formulaInfos)) + .build(), + NetworkModificationResult.ApplicationStatus.WITH_WARNINGS); + + assertNotNull(getNetwork().getTwoWindingsTransformer(TWT_ID_1).getRatioTapChanger()); + assertNotNull(getNetwork().getTwoWindingsTransformer(TWT_ID_2).getRatioTapChanger()); + assertEquals(2, getNetwork().getTwoWindingsTransformer(TWT_ID_1).getRatioTapChanger().getTapPosition()); + assertEquals(5, getNetwork().getTwoWindingsTransformer(TWT_ID_2).getRatioTapChanger().getTapPosition()); + assertNull(getNetwork().getTwoWindingsTransformer(TWT_ID_4).getRatioTapChanger()); + assertNull(getNetwork().getTwoWindingsTransformer(TWT_ID_6).getRatioTapChanger()); + + wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), List.of(FILTER_ID_1, FILTER_ID_4)), false); + } + + @Override + protected void createEquipments() { + Substation s1 = getNetwork().getSubstation("s1"); + Substation s3 = getNetwork().getSubstation("s3"); + TwoWindingsTransformer twt1 = createTwoWindingsTransformer(s1, TWT_ID_1, TWT_ID_1, 30, 40, 50, 60, 10, 20, 100, 110, + "v1", "v2", + "trf1", 11, ConnectablePosition.Direction.TOP, + "trf1", 22, ConnectablePosition.Direction.BOTTOM); + twt1.setRatedS(11); + addRatioTapChangerSteps(twt1.newRatioTapChanger().setTargetV(50).setLowTapPosition(0).setTapPosition(1).setTargetDeadband(55)); + + TwoWindingsTransformer twt2 = createTwoWindingsTransformer(s1, TWT_ID_2, TWT_ID_2, 35, 45, 55, 65, 15, 25, 105, 115, + "v1", "v4", + "trf1", 33, ConnectablePosition.Direction.TOP, + "trf1", 44, ConnectablePosition.Direction.BOTTOM); + twt2.setRatedS(10); + addRatioTapChangerSteps(twt2.newRatioTapChanger().setTargetV(53).setLowTapPosition(3).setTapPosition(4).setTargetDeadband(58)); + + TwoWindingsTransformer twt3 = createTwoWindingsTransformer(s1, TWT_ID_3, TWT_ID_3, 40, 50, 60, 70, 20, 30, 110, 120, + "v2", "v4", + "trf1", 10, ConnectablePosition.Direction.TOP, + "trf1", 20, ConnectablePosition.Direction.BOTTOM); + twt3.setRatedS(25); + addRatioTapChangerSteps(twt3.newRatioTapChanger().setTargetV(56).setLowTapPosition(0).setTapPosition(1).setTargetDeadband(61)); + + TwoWindingsTransformer twt4 = createTwoWindingsTransformer(s3, TWT_ID_4, TWT_ID_4, 45, 55, 65, 75, 25, 35, 115, 125, + "v5", "v6", + "trf1", 30, ConnectablePosition.Direction.TOP, + "trf1", 40, ConnectablePosition.Direction.BOTTOM); + twt4.setRatedS(15); + addPhaseTapChangerSteps(twt4.newPhaseTapChanger().setRegulationValue(45).setLowTapPosition(1).setTapPosition(2).setTargetDeadband(34)); + + TwoWindingsTransformer twt5 = createTwoWindingsTransformer(s3, TWT_ID_5, TWT_ID_5, 50, 60, 70, 80, 30, 40, 120, 130, + "v5", "v6", + "trf1", 15, ConnectablePosition.Direction.TOP, + "trf1", 26, ConnectablePosition.Direction.BOTTOM); + twt5.setRatedS(30); + addPhaseTapChangerSteps(twt5.newPhaseTapChanger().setRegulationValue(46).setLowTapPosition(2).setTapPosition(2).setTargetDeadband(35)); + + TwoWindingsTransformer twt6 = createTwoWindingsTransformer(s3, TWT_ID_6, TWT_ID_6, 55, 65, 75, 85, 35, 45, 125, 135, + "v5", "v6", + "trf1", 38, ConnectablePosition.Direction.TOP, + "trf1", 49, ConnectablePosition.Direction.BOTTOM); + twt6.setRatedS(20); + addPhaseTapChangerSteps(twt6.newPhaseTapChanger().setRegulationValue(47).setLowTapPosition(1).setTapPosition(1).setTargetDeadband(36)); + } + + @Override + protected List getTestFilters() { + IdentifiableAttributes twt1 = getIdentifiableAttributes(TWT_ID_1, 1.0); + IdentifiableAttributes twt2 = getIdentifiableAttributes(TWT_ID_2, 2.0); + IdentifiableAttributes twt3 = getIdentifiableAttributes(TWT_ID_3, 2.0); + IdentifiableAttributes twt4 = getIdentifiableAttributes(TWT_ID_4, 5.0); + IdentifiableAttributes twt5 = getIdentifiableAttributes(TWT_ID_5, 6.0); + IdentifiableAttributes twt6 = getIdentifiableAttributes(TWT_ID_6, 7.0); + + FilterEquipments filter1 = getFilterEquipments(FILTER_ID_1, "filter1", List.of(twt1, twt2), List.of()); + FilterEquipments filter2 = getFilterEquipments(FILTER_ID_2, "filter2", List.of(twt1, twt3), List.of()); + FilterEquipments filter3 = getFilterEquipments(FILTER_ID_3, "filter3", List.of(twt4, twt5), List.of()); + FilterEquipments filter4 = getFilterEquipments(FILTER_ID_4, "filter4", List.of(twt4, twt6), List.of()); + + return List.of(filter1, filter2, filter3, filter4); + } + + @Override + protected List getFormulaInfos() { + FormulaInfos formulaInfos1 = getFormulaInfo(TwoWindingsTransformerField.TARGET_V.name(), + List.of(filter1), + Operator.PERCENTAGE, + ReferenceFieldOrValue.builder().value(200.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.TARGET_V.name()).build()); + + FormulaInfos formulaInfos2 = getFormulaInfo(TwoWindingsTransformerField.RATIO_TAP_POSITION.name(), + List.of(filter2), + Operator.MULTIPLICATION, + ReferenceFieldOrValue.builder().value(4.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.RATIO_TAP_POSITION.name()).build()); + + FormulaInfos formulaInfos3 = getFormulaInfo(TwoWindingsTransformerField.RATIO_LOW_TAP_POSITION.name(), + List.of(filter2), + Operator.ADDITION, + ReferenceFieldOrValue.builder().value(1.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.RATIO_LOW_TAP_POSITION.name()).build()); + + FormulaInfos formulaInfos4 = getFormulaInfo(TwoWindingsTransformerField.RATIO_TARGET_DEADBAND.name(), + List.of(filter1), + Operator.DIVISION, + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.RATIO_TARGET_DEADBAND.name()).build(), + ReferenceFieldOrValue.builder().value(5.).build()); + + FormulaInfos formulaInfos5 = getFormulaInfo(TwoWindingsTransformerField.REGULATION_VALUE.name(), + List.of(filter4), + Operator.PERCENTAGE, + ReferenceFieldOrValue.builder().value(200.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.REGULATION_VALUE.name()).build()); + + FormulaInfos formulaInfos6 = getFormulaInfo(TwoWindingsTransformerField.PHASE_TAP_POSITION.name(), + List.of(filter3), + Operator.MULTIPLICATION, + ReferenceFieldOrValue.builder().value(2.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.PHASE_TAP_POSITION.name()).build()); + + FormulaInfos formulaInfos7 = getFormulaInfo(TwoWindingsTransformerField.PHASE_LOW_TAP_POSITION.name(), + List.of(filter3), + Operator.MULTIPLICATION, + ReferenceFieldOrValue.builder().value(2.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.PHASE_LOW_TAP_POSITION.name()).build()); + + FormulaInfos formulaInfos8 = getFormulaInfo(TwoWindingsTransformerField.PHASE_TARGET_DEADBAND.name(), + List.of(filter4), + Operator.SUBTRACTION, + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.PHASE_TARGET_DEADBAND.name()).build(), + ReferenceFieldOrValue.builder().value(10.).build()); + + FormulaInfos formulaInfos9 = getFormulaInfo(TwoWindingsTransformerField.SERIES_REACTANCE.name(), + List.of(filter1, filter4), + Operator.ADDITION, + ReferenceFieldOrValue.builder().value(20.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.SERIES_REACTANCE.name()).build()); + + FormulaInfos formulaInfos10 = getFormulaInfo(TwoWindingsTransformerField.SERIES_RESISTANCE.name(), + List.of(filter2, filter3), + Operator.PERCENTAGE, + ReferenceFieldOrValue.builder().value(200.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.SERIES_RESISTANCE.name()).build()); + + FormulaInfos formulaInfos11 = getFormulaInfo(TwoWindingsTransformerField.MAGNETIZING_CONDUCTANCE.name(), + List.of(filter4, filter2), + Operator.ADDITION, + ReferenceFieldOrValue.builder().value(25.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.MAGNETIZING_CONDUCTANCE.name()).build()); + + FormulaInfos formulaInfos12 = getFormulaInfo(TwoWindingsTransformerField.MAGNETIZING_SUSCEPTANCE.name(), + List.of(filter1, filter3), + Operator.MULTIPLICATION, + ReferenceFieldOrValue.builder().value(2.5).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.MAGNETIZING_SUSCEPTANCE.name()).build()); + + FormulaInfos formulaInfos13 = getFormulaInfo(TwoWindingsTransformerField.RATED_VOLTAGE_1.name(), + List.of(filter2), + Operator.ADDITION, + ReferenceFieldOrValue.builder().value(15.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.RATED_VOLTAGE_1.name()).build()); + + FormulaInfos formulaInfos14 = getFormulaInfo(TwoWindingsTransformerField.RATED_VOLTAGE_2.name(), + List.of(filter3, filter2), + Operator.PERCENTAGE, + ReferenceFieldOrValue.builder().value(50.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.RATED_VOLTAGE_2.name()).build()); + + FormulaInfos formulaInfos15 = getFormulaInfo(TwoWindingsTransformerField.RATED_S.name(), + List.of(filter1, filter2), + Operator.PERCENTAGE, + ReferenceFieldOrValue.builder().value(200.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.RATED_S.name()).build()); + + return List.of(formulaInfos1, + formulaInfos2, + formulaInfos3, + formulaInfos4, + formulaInfos5, + formulaInfos6, + formulaInfos7, + formulaInfos8, + formulaInfos9, + formulaInfos10, + formulaInfos11, + formulaInfos12, + formulaInfos13, + formulaInfos14, + formulaInfos15); + } + + @Override + protected List getUpdatedFormulaInfos() { + FormulaInfos formulaInfos1 = getFormulaInfo(TwoWindingsTransformerField.TARGET_V.name(), + List.of(filter3), + Operator.PERCENTAGE, + ReferenceFieldOrValue.builder().value(200.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.TARGET_V.name()).build()); + + FormulaInfos formulaInfos2 = getFormulaInfo(TwoWindingsTransformerField.RATIO_TAP_POSITION.name(), + List.of(filter2), + Operator.MULTIPLICATION, + ReferenceFieldOrValue.builder().value(3.5).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.RATIO_TAP_POSITION.name()).build()); + + FormulaInfos formulaInfos3 = getFormulaInfo(TwoWindingsTransformerField.RATIO_LOW_TAP_POSITION.name(), + List.of(filter1), + Operator.MULTIPLICATION, + ReferenceFieldOrValue.builder().value(3.).build(), + ReferenceFieldOrValue.builder().equipmentField(TwoWindingsTransformerField.RATIO_LOW_TAP_POSITION.name()).build()); + + return List.of(formulaInfos1, + formulaInfos2, + formulaInfos3); + } + + @Override + protected IdentifiableType getIdentifiableType() { + return IdentifiableType.TWO_WINDINGS_TRANSFORMER; + } + + @Override + protected void assertAfterNetworkModificationCreation() { + TwoWindingsTransformer twt1 = getNetwork().getTwoWindingsTransformer(TWT_ID_1); + RatioTapChanger ratioTapChanger1 = twt1.getRatioTapChanger(); + assertNotNull(ratioTapChanger1); + assertEquals(100, ratioTapChanger1.getTargetV(), 0); + assertEquals(1, ratioTapChanger1.getLowTapPosition()); + assertEquals(4, ratioTapChanger1.getTapPosition()); + assertEquals(11, ratioTapChanger1.getTargetDeadband(), 0); + assertEquals(60, twt1.getX(), 0); + assertEquals(150, twt1.getB(), 0); + assertEquals(60, twt1.getR(), 0); + assertEquals(75, twt1.getG(), 0); + assertEquals(25, twt1.getRatedU1(), 0); + assertEquals(10, twt1.getRatedU2(), 0); + assertEquals(44, twt1.getRatedS(), 0); + + TwoWindingsTransformer twt2 = getNetwork().getTwoWindingsTransformer(TWT_ID_2); + RatioTapChanger ratioTapChanger2 = twt2.getRatioTapChanger(); + assertNotNull(ratioTapChanger2); + assertEquals(106, ratioTapChanger2.getTargetV(), 0); + assertEquals(3, ratioTapChanger2.getLowTapPosition()); + assertEquals(4, ratioTapChanger2.getTapPosition()); + assertEquals(11.6, ratioTapChanger2.getTargetDeadband(), 0); + assertEquals(65, twt2.getX(), 0); + assertEquals(162.5, twt2.getB(), 0); + assertEquals(15, twt2.getRatedU1(), 0); + assertEquals(20, twt2.getRatedS(), 0); + + TwoWindingsTransformer twt3 = getNetwork().getTwoWindingsTransformer(TWT_ID_3); + RatioTapChanger ratioTapChanger3 = twt3.getRatioTapChanger(); + assertNotNull(ratioTapChanger3); + assertEquals(1, ratioTapChanger3.getLowTapPosition()); + assertEquals(4, ratioTapChanger3.getTapPosition()); + assertEquals(80, twt3.getR(), 0); + assertEquals(85, twt3.getG(), 0); + assertEquals(35, twt3.getRatedU1(), 0); + assertEquals(15, twt3.getRatedU2(), 0); + assertEquals(50, twt3.getRatedS(), 0); + + TwoWindingsTransformer twt4 = getNetwork().getTwoWindingsTransformer(TWT_ID_4); + PhaseTapChanger phaseTapChanger4 = twt4.getPhaseTapChanger(); + assertNotNull(phaseTapChanger4); + assertEquals(90, phaseTapChanger4.getRegulationValue(), 0); + assertEquals(2, phaseTapChanger4.getLowTapPosition()); + assertEquals(4, phaseTapChanger4.getTapPosition()); + assertEquals(24, phaseTapChanger4.getTargetDeadband(), 0); + assertEquals(90, twt4.getR(), 0); + assertEquals(75, twt4.getX(), 0); + assertEquals(90, twt4.getG(), 0); + assertEquals(187.5, twt4.getB(), 0); + assertEquals(25, twt4.getRatedU1(), 0); + assertEquals(17.5, twt4.getRatedU2(), 0); + assertEquals(15, twt4.getRatedS(), 0); + + TwoWindingsTransformer twt5 = getNetwork().getTwoWindingsTransformer(TWT_ID_5); + PhaseTapChanger phaseTapChanger5 = twt5.getPhaseTapChanger(); + assertNotNull(phaseTapChanger4); + assertEquals(4, phaseTapChanger5.getLowTapPosition()); + assertEquals(4, phaseTapChanger5.getTapPosition()); + assertEquals(100, twt5.getR(), 0); + assertEquals(200, twt5.getB(), 0); + assertEquals(20, twt5.getRatedU2(), 0); + + TwoWindingsTransformer twt6 = getNetwork().getTwoWindingsTransformer(TWT_ID_6); + PhaseTapChanger phaseTapChanger6 = twt6.getPhaseTapChanger(); + assertNotNull(phaseTapChanger4); + assertEquals(94, phaseTapChanger6.getRegulationValue(), 0); + assertEquals(26, phaseTapChanger6.getTargetDeadband(), 0); + assertEquals(85, twt6.getX(), 0); + assertEquals(100, twt6.getG(), 0); + } + + @Override + protected void assertAfterNetworkModificationDeletion() { + TwoWindingsTransformer twt1 = getNetwork().getTwoWindingsTransformer(TWT_ID_1); + RatioTapChanger ratioTapChanger1 = twt1.getRatioTapChanger(); + assertNotNull(ratioTapChanger1); + assertEquals(50, ratioTapChanger1.getTargetV(), 0); + assertEquals(0, ratioTapChanger1.getLowTapPosition()); + assertEquals(1, ratioTapChanger1.getTapPosition()); + assertEquals(55, ratioTapChanger1.getTargetDeadband(), 0); + assertEquals(40, twt1.getX(), 0); + assertEquals(60, twt1.getB(), 0); + assertEquals(30, twt1.getR(), 0); + assertEquals(50, twt1.getG(), 0); + assertEquals(10, twt1.getRatedU1(), 0); + assertEquals(20, twt1.getRatedU2(), 0); + assertEquals(11, twt1.getRatedS(), 0); + + TwoWindingsTransformer twt2 = getNetwork().getTwoWindingsTransformer(TWT_ID_2); + RatioTapChanger ratioTapChanger2 = twt2.getRatioTapChanger(); + assertNotNull(ratioTapChanger2); + assertEquals(53, ratioTapChanger2.getTargetV(), 0); + assertEquals(3, ratioTapChanger2.getLowTapPosition()); + assertEquals(4, ratioTapChanger2.getTapPosition()); + assertEquals(58, ratioTapChanger2.getTargetDeadband(), 0); + assertEquals(45, twt2.getX(), 0); + assertEquals(65, twt2.getB(), 0); + assertEquals(15, twt2.getRatedU1(), 0); + assertEquals(10, twt2.getRatedS(), 0); + + TwoWindingsTransformer twt3 = getNetwork().getTwoWindingsTransformer(TWT_ID_3); + RatioTapChanger ratioTapChanger3 = twt3.getRatioTapChanger(); + assertNotNull(ratioTapChanger3); + assertEquals(0, ratioTapChanger3.getLowTapPosition()); + assertEquals(1, ratioTapChanger3.getTapPosition()); + assertEquals(40, twt3.getR(), 0); + assertEquals(60, twt3.getG(), 0); + assertEquals(20, twt3.getRatedU1(), 0); + assertEquals(30, twt3.getRatedU2(), 0); + assertEquals(25, twt3.getRatedS(), 0); + + TwoWindingsTransformer twt4 = getNetwork().getTwoWindingsTransformer(TWT_ID_4); + PhaseTapChanger phaseTapChanger4 = twt4.getPhaseTapChanger(); + assertNotNull(phaseTapChanger4); + assertEquals(45, phaseTapChanger4.getRegulationValue(), 0); + assertEquals(1, phaseTapChanger4.getLowTapPosition()); + assertEquals(2, phaseTapChanger4.getTapPosition()); + assertEquals(34, phaseTapChanger4.getTargetDeadband(), 0); + assertEquals(45, twt4.getR(), 0); + assertEquals(55, twt4.getX(), 0); + assertEquals(65, twt4.getG(), 0); + assertEquals(75, twt4.getB(), 0); + assertEquals(25, twt4.getRatedU1(), 0); + assertEquals(35, twt4.getRatedU2(), 0); + assertEquals(15, twt4.getRatedS(), 0); + + TwoWindingsTransformer twt5 = getNetwork().getTwoWindingsTransformer(TWT_ID_5); + PhaseTapChanger phaseTapChanger5 = twt5.getPhaseTapChanger(); + assertNotNull(phaseTapChanger4); + assertEquals(2, phaseTapChanger5.getLowTapPosition()); + assertEquals(2, phaseTapChanger5.getTapPosition()); + assertEquals(50, twt5.getR(), 0); + assertEquals(80, twt5.getB(), 0); + assertEquals(40, twt5.getRatedU2(), 0); + + TwoWindingsTransformer twt6 = getNetwork().getTwoWindingsTransformer(TWT_ID_6); + PhaseTapChanger phaseTapChanger6 = twt6.getPhaseTapChanger(); + assertNotNull(phaseTapChanger4); + assertEquals(47, phaseTapChanger6.getRegulationValue(), 0); + assertEquals(36, phaseTapChanger6.getTargetDeadband(), 0); + assertEquals(65, twt6.getX(), 0); + assertEquals(75, twt6.getG(), 0); + } + + private void addRatioTapChangerSteps(RatioTapChangerAdder ratioTapChangerAdder) { + ratioTapChangerAdder.beginStep() + .setR(39.78473) + .setX(39.784725) + .setG(0.0) + .setB(0.0) + .setRho(15.0) + .endStep() + .beginStep() + .setR(39.78474) + .setX(39.784726) + .setG(0.0) + .setB(0.0) + .setRho(15.0) + .endStep() + .beginStep() + .setR(39.78473) + .setX(39.784725) + .setG(0.0) + .setB(0.0) + .setRho(15.0) + .endStep() + .beginStep() + .setR(39.78474) + .setX(39.784726) + .setG(0.0) + .setB(0.0) + .setRho(15.0) + .endStep() + .beginStep() + .setR(39.78473) + .setX(39.784725) + .setG(0.0) + .setB(0.0) + .setRho(5.0) + .endStep() + .beginStep() + .setR(39.78474) + .setX(39.784726) + .setG(0.0) + .setB(0.0) + .setRho(15.0) + .endStep() + .add(); + } + + private void addPhaseTapChangerSteps(PhaseTapChangerAdder phaseTapChangerAdder) { + phaseTapChangerAdder.beginStep() + .setR(39.78473) + .setX(39.784725) + .setG(0.0) + .setB(0.0) + .setRho(15.0) + .setAlpha(1.) + .endStep() + .beginStep() + .setR(39.78475) + .setX(39.784727) + .setG(0.0) + .setB(0.0) + .setRho(15.0) + .setAlpha(1.1) + .endStep() + .beginStep() + .setR(39.78473) + .setX(39.784725) + .setG(0.0) + .setB(0.0) + .setRho(15.0) + .setAlpha(1.) + .endStep() + .beginStep() + .setR(39.78475) + .setX(39.784727) + .setG(0.0) + .setB(0.0) + .setRho(15.0) + .setAlpha(1.1) + .endStep() + .beginStep() + .setR(39.78473) + .setX(39.784725) + .setG(0.0) + .setB(0.0) + .setRho(15.0) + .setAlpha(1.) + .endStep() + .beginStep() + .setR(39.78475) + .setX(39.784727) + .setG(0.0) + .setB(0.0) + .setRho(15.0) + .setAlpha(1.1) + .endStep() + .add(); + } +} diff --git a/src/test/java/org/gridsuite/modification/server/modifications/tabularmodifications/TabularVoltageLevelModificationsTest.java b/src/test/java/org/gridsuite/modification/server/modifications/tabularmodifications/TabularVoltageLevelModificationsTest.java new file mode 100644 index 000000000..4851d64d5 --- /dev/null +++ b/src/test/java/org/gridsuite/modification/server/modifications/tabularmodifications/TabularVoltageLevelModificationsTest.java @@ -0,0 +1,96 @@ +/** + * 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.server.modifications.tabularmodifications; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.powsybl.iidm.network.Network; +import lombok.SneakyThrows; +import org.gridsuite.modification.server.dto.*; +import org.gridsuite.modification.server.modifications.AbstractNetworkModificationTest; +import org.gridsuite.modification.server.utils.NetworkCreation; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; + +/** + * @author AJELLAL Ali + */ +@Tag("IntegrationTest") +public class TabularVoltageLevelModificationsTest extends AbstractNetworkModificationTest { + @Override + protected Network createNetwork(UUID networkUuid) { + return NetworkCreation.create(networkUuid, true); + } + + @Override + protected ModificationInfos buildModification() { + List modifications = List.of( + VoltageLevelModificationInfos.builder().equipmentId("v1").nominalVoltage(new AttributeModification<>(300., OperationType.SET)).highVoltageLimit(new AttributeModification<>(400., OperationType.SET)).lowVoltageLimit(new AttributeModification<>(299., OperationType.SET)).build(), + VoltageLevelModificationInfos.builder().equipmentId("v2").nominalVoltage(new AttributeModification<>(300., OperationType.SET)).highVoltageLimit(new AttributeModification<>(400., OperationType.SET)).lowVoltageLimit(new AttributeModification<>(299., OperationType.SET)).build() + ); + return TabularModificationInfos.builder() + .modificationType("VOLTAGE_LEVEL_MODIFICATION") + .modifications(modifications) + .stashed(false) + .build(); + } + + @Override + protected ModificationInfos buildModificationUpdate() { + List modifications = List.of( + VoltageLevelModificationInfos.builder().equipmentId("v1").nominalVoltage(new AttributeModification<>(500., OperationType.SET)).highVoltageLimit(new AttributeModification<>(502., OperationType.SET)).lowVoltageLimit(new AttributeModification<>(499., OperationType.SET)).build(), + VoltageLevelModificationInfos.builder().equipmentId("v2").nominalVoltage(new AttributeModification<>(500., OperationType.SET)).highVoltageLimit(new AttributeModification<>(502., OperationType.SET)).lowVoltageLimit(new AttributeModification<>(499., OperationType.SET)).build() + ); + return TabularModificationInfos.builder() + .modificationType("VOLTAGE_LEVEL_MODIFICATION") + .modifications(modifications) + .stashed(false) + .build(); + } + + @Override + protected void assertAfterNetworkModificationCreation() { + assertEquals(300., getNetwork().getVoltageLevel("v1").getNominalV(), 0.001); + assertEquals(299., getNetwork().getVoltageLevel("v1").getLowVoltageLimit(), 0.001); + assertEquals(400., getNetwork().getVoltageLevel("v1").getHighVoltageLimit(), 0.001); + assertEquals(300., getNetwork().getVoltageLevel("v2").getNominalV(), 0.001); + assertEquals(299., getNetwork().getVoltageLevel("v2").getLowVoltageLimit(), 0.001); + assertEquals(400., getNetwork().getVoltageLevel("v2").getHighVoltageLimit(), 0.001); + } + + @Override + protected void assertAfterNetworkModificationDeletion() { + assertEquals(380.0, getNetwork().getVoltageLevel("v1").getNominalV(), 0.001); + assertEquals(Double.NaN, getNetwork().getVoltageLevel("v1").getLowVoltageLimit(), 0.001); + assertEquals(Double.NaN, getNetwork().getVoltageLevel("v1").getHighVoltageLimit(), 0.001); + assertEquals(225.0, getNetwork().getVoltageLevel("v2").getNominalV(), 0.001); + assertEquals(Double.NaN, getNetwork().getVoltageLevel("v2").getLowVoltageLimit(), 0.001); + assertEquals(Double.NaN, getNetwork().getVoltageLevel("v2").getHighVoltageLimit(), 0.001); + } + + @Override + @SneakyThrows + protected void testCreationModificationMessage(ModificationInfos modificationInfos) { + assertEquals("TABULAR_MODIFICATION", modificationInfos.getMessageType()); + Map createdValues = mapper.readValue(modificationInfos.getMessageValues(), new TypeReference<>() { }); + Assertions.assertEquals("VOLTAGE_LEVEL_MODIFICATION", createdValues.get("tabularModificationType")); + } + + @Override + @SneakyThrows + protected void testUpdateModificationMessage(ModificationInfos modificationInfos) { + assertEquals("TABULAR_MODIFICATION", modificationInfos.getMessageType()); + Map updatedValues = mapper.readValue(modificationInfos.getMessageValues(), new TypeReference<>() { }); + Assertions.assertEquals("VOLTAGE_LEVEL_MODIFICATION", updatedValues.get("tabularModificationType")); + } +} diff --git a/src/test/java/org/gridsuite/modification/server/service/ModificationElasticsearchTest.java b/src/test/java/org/gridsuite/modification/server/service/ModificationElasticsearchTest.java new file mode 100644 index 000000000..100265029 --- /dev/null +++ b/src/test/java/org/gridsuite/modification/server/service/ModificationElasticsearchTest.java @@ -0,0 +1,147 @@ +package org.gridsuite.modification.server.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.powsybl.iidm.network.LoadType; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.VariantManagerConstants; +import com.powsybl.network.store.client.NetworkStoreService; +import com.powsybl.network.store.client.PreloadingStrategy; +import org.gridsuite.modification.server.dto.LoadCreationInfos; +import org.gridsuite.modification.server.dto.LoadModificationInfos; +import org.gridsuite.modification.server.dto.VoltageLevelModificationInfos; +import org.gridsuite.modification.server.elasticsearch.EquipmentInfosRepository; +import org.gridsuite.modification.server.elasticsearch.EquipmentInfosService; +import org.gridsuite.modification.server.repositories.ModificationRepository; +import org.gridsuite.modification.server.utils.ModificationCreation; +import org.gridsuite.modification.server.utils.NetworkCreation; +import org.junit.Before; +import org.junit.jupiter.api.Tag; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.stubbing.Answer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.List; +import java.util.UUID; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@AutoConfigureMockMvc +@SpringBootTest +@Tag("IntegrationTest") +public class ModificationElasticsearchTest { + private static UUID NETWORK_UUID = UUID.randomUUID(); + private static final UUID TEST_GROUP_ID = UUID.randomUUID(); + private static final UUID TEST_REPORT_ID = UUID.randomUUID(); + private static final String URI_NETWORK_MODIF_BASE = "/v1/network-modifications"; + private static final String URI_NETWORK_MODIF_PARAMS = "&groupUuid=" + TEST_GROUP_ID + "&reportUuid=" + TEST_REPORT_ID + "&reporterId=" + UUID.randomUUID(); + private static final String URI_NETWORK_MODIF = URI_NETWORK_MODIF_BASE + "?networkUuid=" + NETWORK_UUID + URI_NETWORK_MODIF_PARAMS; + + private static final String NEW_VARIANT = "NewVariant"; + private static final String NEW_VARIANT_2 = "NewVariant2"; + + @Autowired + MockMvc mockMvc; + + @Autowired + private ObjectMapper mapper; + + @Autowired + EquipmentInfosService equipmentInfosService; + + @Autowired + ModificationRepository modificationRepository; + + @MockBean + NetworkStoreService networkStoreService; + + @MockBean + ReportService reportService; + + @Autowired + private EquipmentInfosRepository equipmentInfosRepository; + + Network network; + + @Before + public void setUp() { + network = NetworkCreation.create(NETWORK_UUID, true); + when(networkStoreService.getNetwork(eq(NETWORK_UUID), nullable(PreloadingStrategy.class))).then((Answer) invocation -> network); + + // clean DB + modificationRepository.deleteAll(); + equipmentInfosService.deleteAll(); + } + + @Test + public void testModificationsToImpactElasticsearch() throws Exception { + network.getVariantManager().cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, NEW_VARIANT); + network.getVariantManager().setWorkingVariant(NEW_VARIANT); + + // load creation - assert name and vl name values + LoadCreationInfos loadCreationInfos = ModificationCreation.getCreationLoad("v1", "v1Load", "v1load_name", "1.1", LoadType.UNDEFINED); + String loadCreationJson = mapper.writeValueAsString(loadCreationInfos); + mockMvc.perform(post(URI_NETWORK_MODIF).content(loadCreationJson).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); + assertEquals("v1load_name", equipmentInfosRepository.findByIdInAndNetworkUuidAndVariantId(List.of("v1Load"), NETWORK_UUID, NEW_VARIANT).get(0).getName()); + assertTrue(equipmentInfosRepository.findByIdInAndNetworkUuidAndVariantId(List.of("v1Load"), NETWORK_UUID, NEW_VARIANT).get(0).getVoltageLevels().stream().anyMatch(vl -> vl.getName().equals("v1"))); + + // load modification - assert name modification + LoadModificationInfos loadModification = ModificationCreation.getModificationLoad("v1Load", null, "v1load_newname", null, null, null, null); + String loadModificationJson = mapper.writeValueAsString(loadModification); + mockMvc.perform(post(URI_NETWORK_MODIF).content(loadModificationJson).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); + assertEquals("v1load_newname", equipmentInfosRepository.findByIdInAndNetworkUuidAndVariantId(List.of("v1Load"), NETWORK_UUID, NEW_VARIANT).get(0).getName()); + } + + @Test + public void testVLModificationsToImpactElasticsearch() throws Exception { + network.getVariantManager().cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, NEW_VARIANT); + network.getVariantManager().setWorkingVariant(NEW_VARIANT); + + // vl modification - assert vl name modification, linked load modification and parent subsation modification + VoltageLevelModificationInfos vlModification = ModificationCreation.getModificationVoltageLevel("v1", "v1_newname"); + String vlModificationJson = mapper.writeValueAsString(vlModification); + mockMvc.perform(post(URI_NETWORK_MODIF).content(vlModificationJson).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); + + checkSomeEquipmentsVoltageLevel(NETWORK_UUID, NEW_VARIANT, "v1_newname"); + + // in a new variant, load modification - assert name modification - assert old data are still here + network.getVariantManager().cloneVariant(NEW_VARIANT, NEW_VARIANT_2); + network.getVariantManager().setWorkingVariant(NEW_VARIANT_2); + // vl modification - assert vl name modification, linked load modification and parent subsation modification + VoltageLevelModificationInfos vlModification2 = ModificationCreation.getModificationVoltageLevel("v1", "v1_newname_2"); + String vlModification2Json = mapper.writeValueAsString(vlModification2); + mockMvc.perform(post(URI_NETWORK_MODIF).content(vlModification2Json).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); + + checkSomeEquipmentsVoltageLevel(NETWORK_UUID, NEW_VARIANT, "v1_newname"); + checkSomeEquipmentsVoltageLevel(NETWORK_UUID, NEW_VARIANT_2, "v1_newname_2"); + } + + private void checkSomeEquipmentsVoltageLevel(UUID networkUuid, String variantId, String voltageLevelName) { + // assert targeted voltage level has been updated + assertEquals(voltageLevelName, equipmentInfosRepository.findByIdInAndNetworkUuidAndVariantId(List.of("v1"), networkUuid, variantId).get(0).getName()); + + // assert linked load has been updated (linked connectable) + assertTrue(checkEquipmentHasVoltageLevelWithName(networkUuid, variantId, "v1Load", voltageLevelName)); + + // assert linked switch has been updated (linked equipment which is not a connectable) + assertTrue(checkEquipmentHasVoltageLevelWithName(networkUuid, variantId, "v1dlcc", voltageLevelName)); + + // assert parent substation has been updated + assertTrue(checkEquipmentHasVoltageLevelWithName(networkUuid, variantId, "s1", voltageLevelName)); + } + + private boolean checkEquipmentHasVoltageLevelWithName(UUID networkUuid, String variantId, String equipmentId, String voltageLevelName) { + return equipmentInfosRepository.findByIdInAndNetworkUuidAndVariantId(List.of(equipmentId), networkUuid, variantId).get(0).getVoltageLevels().stream().anyMatch(vl -> vl.getName().equals(voltageLevelName)); + } +} diff --git a/src/test/java/org/gridsuite/modification/server/utils/ModificationCreation.java b/src/test/java/org/gridsuite/modification/server/utils/ModificationCreation.java index ca7b82248..76f661ec4 100644 --- a/src/test/java/org/gridsuite/modification/server/utils/ModificationCreation.java +++ b/src/test/java/org/gridsuite/modification/server/utils/ModificationCreation.java @@ -8,6 +8,7 @@ package org.gridsuite.modification.server.utils; import com.powsybl.iidm.network.EnergySource; +import com.powsybl.iidm.network.LoadType; import com.powsybl.iidm.network.SwitchKind; import com.powsybl.iidm.network.extensions.ConnectablePosition; import org.gridsuite.modification.server.dto.*; @@ -94,4 +95,61 @@ public static GeneratorCreationInfos getCreationGenerator(String vlId, String ge .connectionDirection(ConnectablePosition.Direction.TOP) .build(); } + + public static LoadCreationInfos getCreationLoad(String vlId, String loadId, String loadName, String busOrBusBarSectionId, LoadType loadType) { + return LoadCreationInfos.builder() + .stashed(false) + .equipmentId(loadId) + .equipmentName(loadName) + .voltageLevelId(vlId) + .busOrBusbarSectionId(busOrBusBarSectionId) + .loadType(loadType) + .activePower(100.0) + .reactivePower(20.0) + .connectionName("top") + .connectionDirection(ConnectablePosition.Direction.TOP) + .build(); + } + + public static LoadModificationInfos getModificationLoad(String loadId, String vlId, String loadName, String busOrBusbarSectionId, LoadType loadType, Long activePower, Long reactivePower) { + LoadModificationInfos.LoadModificationInfosBuilder builder = LoadModificationInfos.builder() + .stashed(false) + .equipmentId(loadId); + + if (loadName != null) { + builder.equipmentName(AttributeModification.toAttributeModification(loadName, OperationType.SET)); + } + + if (vlId != null) { + builder.voltageLevelId(AttributeModification.toAttributeModification(vlId, OperationType.SET)); + } + + if (busOrBusbarSectionId != null) { + builder.busOrBusbarSectionId(AttributeModification.toAttributeModification(busOrBusbarSectionId, OperationType.SET)); + } + + if (loadType != null) { + builder.loadType(AttributeModification.toAttributeModification(LoadType.UNDEFINED, OperationType.SET)); + } + + if (activePower != null) { + builder.activePower(AttributeModification.toAttributeModification(activePower, OperationType.SET)); + } + + if (reactivePower != null) { + builder.reactivePower(AttributeModification.toAttributeModification(reactivePower, OperationType.SET)); + } + + return builder.build(); + } + + public static VoltageLevelModificationInfos getModificationVoltageLevel(String vlId, String vlName) { + VoltageLevelModificationInfos.VoltageLevelModificationInfosBuilder builder = VoltageLevelModificationInfos.builder() + .stashed(false) + .equipmentId(vlId); + + builder.equipmentName(AttributeModification.toAttributeModification(vlName, OperationType.SET)); + + return builder.build(); + } } diff --git a/src/test/resources/reports_voltage_init_modification_ok.json b/src/test/resources/reports_voltage_init_modification_ok.json index 9b310afa4..5c988de58 100644 --- a/src/test/resources/reports_voltage_init_modification_ok.json +++ b/src/test/resources/reports_voltage_init_modification_ok.json @@ -4,8 +4,7 @@ "taskKey" : "99999999-9999-9999-9999-999999999999@NetworkModification", "subReporters" : [ { "taskKey" : "VOLTAGE_INIT_MODIFICATION", - "reports" : [ - { + "reports" : [ { "reportKey" : "generatorModificationsResume", "values" : { "reportSeverity" : { @@ -62,14 +61,8 @@ } } ], "subReporters" : [ { - "taskKey" : "GeneratorModifications", - "taskValues" : { - "generatorId" : { - "value" : "GTH2" - } - }, - "reports" : [ - { + "taskKey" : "GeneratorsModifications", + "reports" : [ { "reportKey" : "generatorModification", "values" : { "reportSeverity" : { @@ -99,14 +92,8 @@ } } ] }, { - "taskKey" : "2WindingsTransformerModifications", - "taskValues" : { - "transformerId" : { - "value" : "TWT2" - } - }, - "reports" : [ - { + "taskKey" : "2WindingsTransformersModifications", + "reports" : [ { "reportKey" : "2WindingsTransformerModification", "values" : { "reportSeverity" : { @@ -136,12 +123,7 @@ } } ] }, { - "taskKey" : "StaticVarCompensatorModifications", - "taskValues" : { - "compensatorId" : { - "value" : "SVC" - } - }, + "taskKey" : "StaticVarCompensatorsModifications", "reports" : [ { "reportKey" : "staticVarCompensatorModification", "values" : { @@ -172,12 +154,7 @@ } } ] }, { - "taskKey" : "ShuntCompensatorModifications", - "taskValues" : { - "compensatorId" : { - "value" : "SHUNT3" - } - }, + "taskKey" : "ShuntCompensatorsModifications", "reports" : [ { "reportKey" : "shuntCompensatorModification", "values" : { @@ -216,12 +193,7 @@ } } ] }, { - "taskKey" : "VscConverterStationModifications", - "taskValues" : { - "converterId" : { - "value" : "VSC2" - } - }, + "taskKey" : "VscConverterStationsModifications", "reports" : [ { "reportKey" : "vscConverterStationModification", "values" : { @@ -259,22 +231,22 @@ "99999999-9999-9999-9999-999999999999@NetworkModification" : "99999999-9999-9999-9999-999999999999@NetworkModification", "VOLTAGE_INIT_MODIFICATION" : "Voltage init modification", "modification-indent1" : "\t${fieldName} : ${oldValue} → ${newValue}", - "GeneratorModifications" : "Generator ${generatorId}", + "GeneratorsModifications" : "Generators", "generatorModification" : "Generator with id=${id} modified :", "generatorModificationsResume" : "${count} generator(s) have been modified.", - "2WindingsTransformerModifications" : "2 windings transformer ${transformerId}", + "2WindingsTransformersModifications" : "2 windings transformers", "2WindingsTransformerModification" : "2 windings transformer with id=${id} modified :", "windingsTransformerModificationsResume" : "${count} transformer(s) have been modified.", - "VscConverterStationModifications" : "Vsc converter station ${converterId}", + "VscConverterStationsModifications" : "Vsc converter stations", "vscConverterStationModification" : "Vsc converter station with id=${id} modified :", "vscModificationsResume" : "${count} vsc converter station(s) have been modified.", - "StaticVarCompensatorModifications" : "Static var compensator ${compensatorId}", + "StaticVarCompensatorsModifications" : "Static var compensators", "staticVarCompensatorModification" : "Static var compensator with id=${id} modified :", "svcModificationsResume" : "${count} static var compensator(s) have been modified.", - "ShuntCompensatorModifications" : "Shunt compensator ${compensatorId}", + "ShuntCompensatorsModifications" : "Shunt compensators", "shuntCompensatorModification" : "Shunt compensator with id=${id} modified :", "shuntCompensatorDisconnected" : "\tShunt compensator disconnected", "shuntCompensatorModificationsResume" : "${count} shunt compensator(s) have been modified." } } -} \ No newline at end of file +} diff --git a/src/test/resources/reports_voltage_init_modification_warnings.json b/src/test/resources/reports_voltage_init_modification_warnings.json index 0bec6b04d..eb34fd432 100644 --- a/src/test/resources/reports_voltage_init_modification_warnings.json +++ b/src/test/resources/reports_voltage_init_modification_warnings.json @@ -28,12 +28,7 @@ } } ], "subReporters" : [ { - "taskKey" : "GeneratorModifications", - "taskValues" : { - "generatorId" : { - "value" : "G1" - } - }, + "taskKey" : "GeneratorsModifications", "reports" : [ { "reportKey" : "generatorNotFound", "values" : { @@ -45,15 +40,7 @@ "value" : "G1" } } - } ] - }, { - "taskKey" : "GeneratorModifications", - "taskValues" : { - "generatorId" : { - "value" : "G2" - } - }, - "reports" : [ { + }, { "reportKey" : "generatorNotFound", "values" : { "reportSeverity" : { @@ -66,12 +53,7 @@ } } ] }, { - "taskKey" : "2WindingsTransformerModifications", - "taskValues" : { - "transformerId" : { - "value" : "2WT1" - } - }, + "taskKey" : "2WindingsTransformersModifications", "reports" : [ { "reportKey" : "2WindingsTransformerNotFound", "values" : { @@ -85,12 +67,7 @@ } } ] }, { - "taskKey" : "3WindingsTransformerModifications", - "taskValues" : { - "transformerId" : { - "value" : "3WT1" - } - }, + "taskKey" : "3WindingsTransformersModifications", "reports" : [ { "reportKey" : "3WindingsTransformerNotFound", "values" : { @@ -104,12 +81,7 @@ } } ] }, { - "taskKey" : "StaticVarCompensatorModifications", - "taskValues" : { - "compensatorId" : { - "value" : "SVC1" - } - }, + "taskKey" : "StaticVarCompensatorsModifications", "reports" : [ { "reportKey" : "staticVarCompensatorNotFound", "values" : { @@ -121,15 +93,7 @@ "value" : "SVC1" } } - } ] - }, { - "taskKey" : "StaticVarCompensatorModifications", - "taskValues" : { - "compensatorId" : { - "value" : "SVC2" - } - }, - "reports" : [ { + }, { "reportKey" : "staticVarCompensatorNotFound", "values" : { "reportSeverity" : { @@ -142,12 +106,7 @@ } } ] }, { - "taskKey" : "ShuntCompensatorModifications", - "taskValues" : { - "compensatorId" : { - "value" : "v2shunt" - } - }, + "taskKey" : "ShuntCompensatorsModifications", "reports" : [ { "reportKey" : "shuntCompensatorNotFound", "values" : { @@ -159,15 +118,7 @@ "value" : "v2shunt" } } - } ] - }, { - "taskKey" : "ShuntCompensatorModifications", - "taskValues" : { - "compensatorId" : { - "value" : "v5shunt" - } - }, - "reports" : [ { + }, { "reportKey" : "shuntCompensatorNotFound", "values" : { "reportSeverity" : { @@ -178,15 +129,7 @@ "value" : "v5shunt" } } - } ] - }, { - "taskKey" : "ShuntCompensatorModifications", - "taskValues" : { - "compensatorId" : { - "value" : "v6shunt" - } - }, - "reports" : [ { + }, { "reportKey" : "shuntCompensatorNotFound", "values" : { "reportSeverity" : { @@ -199,12 +142,7 @@ } } ] }, { - "taskKey" : "VscConverterStationModifications", - "taskValues" : { - "converterId" : { - "value" : "VSC1" - } - }, + "taskKey" : "VscConverterStationsModifications", "reports" : [ { "reportKey" : "vscConverterStationModification", "values" : { @@ -233,15 +171,7 @@ "value" : "500.0" } } - } ] - }, { - "taskKey" : "VscConverterStationModifications", - "taskValues" : { - "converterId" : { - "value" : "VSC2" - } - }, - "reports" : [ { + }, { "reportKey" : "vscConverterStationModification", "values" : { "reportSeverity" : { @@ -278,20 +208,20 @@ "99999999-9999-9999-9999-999999999999@NetworkModification" : "99999999-9999-9999-9999-999999999999@NetworkModification", "VOLTAGE_INIT_MODIFICATION" : "Voltage init modification", "modification-indent1" : "\t${fieldName} : ${oldValue} → ${newValue}", - "GeneratorModifications" : "Generator ${generatorId}", + "GeneratorsModifications" : "Generators", "generatorNotFound" : "Generator with id=${id} not found", - "2WindingsTransformerModifications" : "2 windings transformer ${transformerId}", + "2WindingsTransformersModifications" : "2 windings transformers", "2WindingsTransformerNotFound" : "2 windings transformer with id=${id} not found", - "3WindingsTransformerModifications" : "3 windings transformer ${transformerId}", + "3WindingsTransformersModifications" : "3 windings transformers", "3WindingsTransformerNotFound" : "3 windings transformer with id=${id} not found", "windingsTransformerModificationsResume" : "${count} transformer(s) have been modified.", - "VscConverterStationModifications" : "Vsc converter station ${converterId}", + "VscConverterStationsModifications" : "Vsc converter stations", "vscConverterStationModification" : "Vsc converter station with id=${id} modified :", "vscModificationsResume" : "${count} vsc converter station(s) have been modified.", - "StaticVarCompensatorModifications" : "Static var compensator ${compensatorId}", + "StaticVarCompensatorsModifications" : "Static var compensators", "staticVarCompensatorNotFound" : "Static var compensator with id=${id} not found", - "ShuntCompensatorModifications" : "Shunt compensator ${compensatorId}", + "ShuntCompensatorsModifications" : "Shunt compensators", "shuntCompensatorNotFound" : "Shunt compensator with id=${id} not found" } } -} \ No newline at end of file +} diff --git a/src/test/resources/testGenerationDispatchWithBatteries.xiidm b/src/test/resources/testGenerationDispatchWithBatteries.xiidm new file mode 100644 index 000000000..15a155c09 --- /dev/null +++ b/src/test/resources/testGenerationDispatchWithBatteries.xiidm @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/testGenerationDispatchWithMultipleEnergySource.xiidm b/src/test/resources/testGenerationDispatchWithMultipleEnergySource.xiidm new file mode 100644 index 000000000..c2d6075b0 --- /dev/null +++ b/src/test/resources/testGenerationDispatchWithMultipleEnergySource.xiidm @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file