From 00789051cb261f2647dad1ebc09a9baf056d7cbc Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Mon, 23 Oct 2023 17:22:50 +0200 Subject: [PATCH 01/20] Add by formula modification --- .../dto/ByFormulaModificationInfos.java | 29 ++++ .../server/dto/formula/FormulaInfos.java | 33 ++++ .../server/dto/formula/Operator.java | 9 + .../dto/formula/ReferenceFieldOrValue.java | 36 ++++ .../formula/equipmentfield/BatteryField.java | 20 +++ .../equipmentfield/EquipmentField.java | 7 + .../equipmentfield/GeneratorField.java | 154 ++++++++++++++++++ .../ByFormulaModificationEntity.java | 47 ++++++ .../equipment/modification/FormulaEntity.java | 54 ++++++ .../modifications/ByFormulaModification.java | 114 +++++++++++++ 10 files changed, 503 insertions(+) create mode 100644 src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java create mode 100644 src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java create mode 100644 src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java create mode 100644 src/main/java/org/gridsuite/modification/server/dto/formula/ReferenceFieldOrValue.java create mode 100644 src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/BatteryField.java create mode 100644 src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/EquipmentField.java create mode 100644 src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java create mode 100644 src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java create mode 100644 src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java create mode 100644 src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java diff --git a/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java new file mode 100644 index 000000000..834942f72 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java @@ -0,0 +1,29 @@ +package org.gridsuite.modification.server.dto; + +import com.powsybl.iidm.network.IdentifiableType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.gridsuite.modification.server.dto.formula.FormulaInfos; +import org.gridsuite.modification.server.entities.equipment.modification.ByFormulaModificationEntity; + +import java.util.List; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class ByFormulaModificationInfos extends ModificationInfos { + @Schema(description = "Identifiable type") + private IdentifiableType identifiableType; + + @Schema(description = "list of formulas") + private List formulaInfosList; + + @Override + public ByFormulaModificationEntity toEntity() { + return new ByFormulaModificationEntity(this); + } +} diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java b/src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java new file mode 100644 index 000000000..7ada7b26f --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java @@ -0,0 +1,33 @@ +package org.gridsuite.modification.server.dto.formula; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.gridsuite.modification.server.dto.FilterEquipments; +import org.gridsuite.modification.server.dto.FilterInfos; +import org.gridsuite.modification.server.dto.formula.equipmentfield.EquipmentField; + +import java.util.List; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class FormulaInfos { + @Schema(description = "List of filters") + private List filters; + + @Schema(description = "Equipment field") + private EquipmentField equipmentField; + + @Schema(description = "First reference field or value") + private ReferenceFieldOrValue fieldOrValue1; + + @Schema(description = "Second reference field or value") + private ReferenceFieldOrValue fieldOrValue2; + + @Schema(description = "Operator") + private Operator operator; +} diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java b/src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java new file mode 100644 index 000000000..55f926622 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java @@ -0,0 +1,9 @@ +package org.gridsuite.modification.server.dto.formula; + +public enum Operator { + ADDITION, + SUBTRACTION, + MULTIPLICATION, + DIVISION, + MODULUS +} 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 new file mode 100644 index 000000000..be05a12d6 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/ReferenceFieldOrValue.java @@ -0,0 +1,36 @@ +package org.gridsuite.modification.server.dto.formula; + +import com.powsybl.iidm.network.Battery; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.IdentifiableType; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.gridsuite.modification.server.dto.formula.equipmentfield.BatteryField; +import org.gridsuite.modification.server.dto.formula.equipmentfield.EquipmentField; +import org.gridsuite.modification.server.dto.formula.equipmentfield.GeneratorField; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class ReferenceFieldOrValue { + private EquipmentField equipmentField; + + private Double value; + + public Double getRefOrValue(Identifiable identifiable) { + if (value != null) { + return value; + } + + IdentifiableType identifiableType = identifiable.getType(); + return switch (identifiableType) { + case GENERATOR -> GeneratorField.getReferenceValue((Generator) identifiable, (GeneratorField) equipmentField); + case BATTERY -> BatteryField.getReferenceValue((Battery) identifiable, (BatteryField) equipmentField); + default -> throw new UnsupportedOperationException("TODO"); + }; + } +} diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/BatteryField.java b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/BatteryField.java new file mode 100644 index 000000000..8d41c04c8 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/BatteryField.java @@ -0,0 +1,20 @@ +package org.gridsuite.modification.server.dto.formula.equipmentfield; + +import com.powsybl.iidm.network.Battery; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.IdentifiableType; + +public enum BatteryField implements EquipmentField { + ACTIVE_POWER; + + @Override + public IdentifiableType getIdentifiableType() { + return IdentifiableType.BATTERY; + } + + public static Double getReferenceValue(Battery battery, BatteryField batteryField) { + return switch (batteryField) { + case ACTIVE_POWER -> battery.getMaxP(); + }; + } +} diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/EquipmentField.java b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/EquipmentField.java new file mode 100644 index 000000000..8de47ef8b --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/EquipmentField.java @@ -0,0 +1,7 @@ +package org.gridsuite.modification.server.dto.formula.equipmentfield; + +import com.powsybl.iidm.network.IdentifiableType; + +public interface EquipmentField { + IdentifiableType getIdentifiableType(); +} diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java new file mode 100644 index 000000000..8f3bc71cb --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java @@ -0,0 +1,154 @@ +package org.gridsuite.modification.server.dto.formula.equipmentfield; + +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.IdentifiableType; +import com.powsybl.iidm.network.MinMaxReactiveLimits; +import com.powsybl.iidm.network.extensions.ActivePowerControl; +import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; +import com.powsybl.iidm.network.extensions.CoordinatedReactiveControl; +import com.powsybl.iidm.network.extensions.CoordinatedReactiveControlAdder; +import com.powsybl.iidm.network.extensions.GeneratorShortCircuit; +import com.powsybl.iidm.network.extensions.GeneratorShortCircuitAdder; +import com.powsybl.iidm.network.extensions.GeneratorStartup; +import com.powsybl.iidm.network.extensions.GeneratorStartupAdder; +import org.gridsuite.modification.server.NetworkModificationException; + +public enum GeneratorField implements EquipmentField { + MINIMUM_ACTIVE_POWER, + MAXIMUM_ACTIVE_POWER, + RATED_NOMINAL_POWER, + ACTIVE_POWER_SET_POINT, + REACTIVE_POWER_SET_POINT, + VOLTAGE_SET_POINT, + PLANNING_ACTIVE_POWER_SET_POINT, + MARGINAL_COST, + PLANNING_OUTAGE_RATE, + FORCED_OUTAGE_RATE, + MINIMUM_REACTIVE_POWER, + MAXIMUM_REACTIVE_POWER, + DROOP, + TRANSIENT_REACTANCE, + STEP_UP_TRANSFORMER_REACTANCE, + Q_PERCENT; + + @Override + public IdentifiableType getIdentifiableType() { + return IdentifiableType.GENERATOR; + } + + public static Double getReferenceValue(Generator generator, GeneratorField generatorField) { + MinMaxReactiveLimits minMaxReactiveLimits = generator.getReactiveLimits(MinMaxReactiveLimits.class); + ActivePowerControl activePowerControl = generator.getExtension(ActivePowerControl.class); + GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); + GeneratorShortCircuit generatorShortCircuit = generator.getExtension(GeneratorShortCircuit.class); + CoordinatedReactiveControl coordinatedReactiveControl = generator.getExtension(CoordinatedReactiveControl.class); + return switch (generatorField) { + case MAXIMUM_ACTIVE_POWER -> generator.getMaxP(); + case MINIMUM_ACTIVE_POWER -> generator.getMinP(); + case ACTIVE_POWER_SET_POINT -> generator.getTargetP(); + case RATED_NOMINAL_POWER -> generator.getRatedS(); + case REACTIVE_POWER_SET_POINT -> generator.getTargetQ(); + case VOLTAGE_SET_POINT -> generator.getTargetV(); + case PLANNING_ACTIVE_POWER_SET_POINT -> generatorStartup != null ? generatorStartup.getPlannedActivePowerSetpoint() : null; + case MARGINAL_COST -> generatorStartup != null ? generatorStartup.getMarginalCost() : null; + case PLANNING_OUTAGE_RATE -> generatorStartup != null ? generatorStartup.getPlannedOutageRate() : null; + case FORCED_OUTAGE_RATE -> generatorStartup != null ? generatorStartup.getForcedOutageRate() : null; + case MINIMUM_REACTIVE_POWER -> minMaxReactiveLimits != null ? minMaxReactiveLimits.getMinQ() : null; + case MAXIMUM_REACTIVE_POWER -> minMaxReactiveLimits != null ? minMaxReactiveLimits.getMaxQ() : null; + case DROOP -> activePowerControl != null ? activePowerControl.getDroop() : null; + case TRANSIENT_REACTANCE -> generatorShortCircuit != null ? generatorShortCircuit.getDirectTransX() : null; + case STEP_UP_TRANSFORMER_REACTANCE -> generatorShortCircuit != null ? generatorShortCircuit.getStepUpTransformerX() : null; + case Q_PERCENT -> coordinatedReactiveControl != null ? coordinatedReactiveControl.getQPercent() : null; + }; + } + + public static void setNewValue(Generator generator, GeneratorField generatorField, Double newValue) { + GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); + GeneratorShortCircuit generatorShortCircuit = generator.getExtension(GeneratorShortCircuit.class); + MinMaxReactiveLimits minMaxReactiveLimits = generator.getReactiveLimits(MinMaxReactiveLimits.class); + ActivePowerControl activePowerControl = generator.getExtension(ActivePowerControl.class); + CoordinatedReactiveControl coordinatedReactiveControl = generator.getExtension(CoordinatedReactiveControl.class); + switch (generatorField) { + case MAXIMUM_ACTIVE_POWER -> generator.setMaxP(newValue); + case MINIMUM_ACTIVE_POWER -> generator.setMinP(newValue); + case ACTIVE_POWER_SET_POINT -> generator.setTargetP(newValue); + case RATED_NOMINAL_POWER -> generator.setRatedS(newValue); + case REACTIVE_POWER_SET_POINT -> generator.setTargetQ(newValue); + case VOLTAGE_SET_POINT -> generator.setTargetV(newValue); + case PLANNING_ACTIVE_POWER_SET_POINT -> { + if (generatorStartup == null) { + generator.newExtension(GeneratorStartupAdder.class) + .withPlannedActivePowerSetpoint(newValue) + .add(); + } else { + generatorStartup.setPlannedActivePowerSetpoint(newValue); + } + } + case MARGINAL_COST -> { + if (generatorStartup == null) { + generator.newExtension(GeneratorStartupAdder.class) + .withMarginalCost(newValue) + .add(); + } else { + generatorStartup.setMarginalCost(newValue); + } + } + case PLANNING_OUTAGE_RATE -> { + if (generatorStartup == null) { + generator.newExtension(GeneratorStartupAdder.class) + .withPlannedOutageRate(newValue) + .add(); + } else { + generatorStartup.setPlannedOutageRate(newValue); + } + } + case FORCED_OUTAGE_RATE -> { + if (generatorStartup == null) { + generator.newExtension(GeneratorStartupAdder.class) + .withForcedOutageRate(newValue) + .add(); + } else { + generatorStartup.setForcedOutageRate(newValue); + } + } + case MINIMUM_REACTIVE_POWER -> { + if (minMaxReactiveLimits ==null) { + throw new NetworkModificationException(NetworkModificationException.Type.MODIFY_GENERATOR_ERROR, "TODO"); + } + generator.newMinMaxReactiveLimits() + .setMinQ(newValue) + .setMaxQ(minMaxReactiveLimits.getMaxQ()) + .add(); + } + case MAXIMUM_REACTIVE_POWER -> { + if (minMaxReactiveLimits ==null) { + throw new NetworkModificationException(NetworkModificationException.Type.MODIFY_GENERATOR_ERROR, "TODO"); + } + generator.newMinMaxReactiveLimits() + .setMaxQ(newValue) + .setMinQ(minMaxReactiveLimits.getMinQ()) + .add(); + } + case DROOP -> { + if (activePowerControl == null) { + generator.newExtension(ActivePowerControlAdder.class) + .withDroop(newValue) + .add(); + } else { + activePowerControl.setDroop(newValue); + } + } + case TRANSIENT_REACTANCE -> generator.newExtension(GeneratorShortCircuitAdder.class) + .withDirectTransX(newValue) + .withStepUpTransformerX(generatorShortCircuit == null ? Double.NaN : generatorShortCircuit.getStepUpTransformerX()) + .add(); + case STEP_UP_TRANSFORMER_REACTANCE -> generator.newExtension(GeneratorShortCircuitAdder.class) + .withDirectTransX(generatorShortCircuit == null ? 0.0D : generatorShortCircuit.getDirectTransX()) + .withStepUpTransformerX(newValue) + .add(); + case Q_PERCENT -> generator.newExtension(CoordinatedReactiveControlAdder.class) + .withQPercent(newValue) + .add(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java new file mode 100644 index 000000000..aab1ebfe0 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java @@ -0,0 +1,47 @@ +package org.gridsuite.modification.server.entities.equipment.modification; + +import com.powsybl.iidm.network.IdentifiableType; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import org.gridsuite.modification.server.dto.ByFormulaModificationInfos; +import org.gridsuite.modification.server.dto.ModificationInfos; +import org.gridsuite.modification.server.entities.ModificationEntity; + +import java.util.List; +import java.util.stream.Collectors; + +@NoArgsConstructor +@Getter +@Entity +@Table(name = "byFormulaModification") +public class ByFormulaModificationEntity extends ModificationEntity { + @Column + private IdentifiableType identifiableType; + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + private List formulaEntities; + + public ByFormulaModificationEntity(ByFormulaModificationInfos byFormulaModificationInfos) { + super(byFormulaModificationInfos); + assignAttributes(byFormulaModificationInfos); + } + + public void update(@NonNull ModificationInfos modificationInfos) { + super.update(modificationInfos); + assignAttributes((ByFormulaModificationInfos) modificationInfos); + } + + private void assignAttributes(ByFormulaModificationInfos byFormulaModificationInfos) { + this.identifiableType = byFormulaModificationInfos.getIdentifiableType(); + this.formulaEntities = byFormulaModificationInfos.getFormulaInfosList().stream() + .map(FormulaEntity::new) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java new file mode 100644 index 000000000..8370a8e0b --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java @@ -0,0 +1,54 @@ +package org.gridsuite.modification.server.entities.equipment.modification; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +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.EquipmentField; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@NoArgsConstructor +@Getter +@Entity +@Table(name = "formula") +public class FormulaEntity { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id") + private UUID id; + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @JoinTable( + joinColumns = @JoinColumn(name = "id"), + inverseJoinColumns = @JoinColumn(name = "filterId")) + private List filters; + + @Column + private String equipmentField1; + + @Column + private Double value1; + + @Column + private String equipmentField2; + + @Column + private Double value2; + + @Column + private Operator operator; + + public FormulaEntity(FormulaInfos formulaInfos) { + this.filters = formulaInfos.getFilters().stream().map(FilterInfos::toEntity).collect(Collectors.toList()); + this.equipmentField1 = formulaInfos.getFieldOrValue1().getEquipmentField().toString(); + this.equipmentField2 = formulaInfos.getFieldOrValue2().getEquipmentField().toString(); + this.value1 = formulaInfos.getFieldOrValue1().getValue(); + this.value2 = formulaInfos.getFieldOrValue2().getValue(); + this.operator = formulaInfos.getOperator(); + } +} diff --git a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java new file mode 100644 index 000000000..3dc427b1a --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -0,0 +1,114 @@ +package org.gridsuite.modification.server.modifications; + +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.commons.reporter.TypedValue; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.Network; +import com.powsybl.network.store.iidm.impl.NetworkImpl; +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.GeneratorField; +import org.gridsuite.modification.server.service.FilterService; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.gridsuite.modification.server.modifications.ModificationUtils.createReport; +import static org.gridsuite.modification.server.modifications.ModificationUtils.distinctByKey; + +public class ByFormulaModification extends AbstractModification { + private ByFormulaModificationInfos modificationInfos; + protected FilterService filterService; + + public ByFormulaModification(ByFormulaModificationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void initApplicationContext(NetworkModificationApplicator modificationApplicator) { + filterService = modificationApplicator.getFilterService(); + } + + @Override + public void apply(Network network, Reporter subReporter) { + // collect all filters from all variations + var filters = modificationInfos.getFormulaInfosList().stream() + .flatMap(v -> v.getFilters().stream()) + .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 = modificationInfos.getErrorType() + ": There is no valid equipment ID among the provided filter(s)"; + createReport(subReporter, "invalidFilters", errorMsg, TypedValue.ERROR_SEVERITY); + return; + } + + // 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); + }); + + modificationInfos.getFormulaInfosList().forEach(formulaInfos -> formulaInfos.getFilters().forEach((filterInfos -> { + var filterEquipments = exportFilters.get(filterInfos.getId()); + filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, attributes.getId(), formulaInfos)); + }))); + } + + private void applyFormula(Network network, String identifiableId, FormulaInfos formulaInfos) { + Identifiable identifiable = network.getIdentifiable(identifiableId); + Double value1 = formulaInfos.getFieldOrValue1().getRefOrValue(identifiable); + Double value2 = formulaInfos.getFieldOrValue2().getRefOrValue(identifiable); + switch (identifiable.getType()) { + case GENERATOR -> { + GeneratorField.setNewValue((Generator) identifiable, + (GeneratorField) formulaInfos.getEquipmentField(), + applyOperation(formulaInfos.getOperator(), value1, value2)); + } + } + } + + private Double applyOperation(Operator operator, Double value1, Double value2) { + if (value1 == null || + value2 == null || + (value2 == 0 && operator == Operator.DIVISION)) { + throw new UnsupportedOperationException("TODO"); + } + + return switch (operator) { + case ADDITION -> value1 + value2; + case SUBTRACTION -> value1 - value2; + case MULTIPLICATION -> value1 * value2; + case DIVISION -> value1 / value2; + case MODULUS -> value1 % value2; + }; + } +} From d06b5db98393877f15cbff2b07834790c4d324ed Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Wed, 25 Oct 2023 22:10:42 +0200 Subject: [PATCH 02/20] changes --- .../modification/server/ModificationType.java | 3 +- .../server/NetworkModificationException.java | 3 +- .../dto/ByFormulaModificationInfos.java | 11 +++ .../formula/equipmentfield/BatteryField.java | 29 ++++++- .../equipmentfield/GeneratorField.java | 75 ++++++++----------- .../equipment/modification/FormulaEntity.java | 1 + .../modifications/ByFormulaModification.java | 13 +++- .../changesets/changelog_20231025T194218Z.xml | 64 ++++++++++++++++ .../db/changelog/db.changelog-master.yaml | 3 + 9 files changed, 152 insertions(+), 50 deletions(-) create mode 100644 src/main/resources/db/changelog/changesets/changelog_20231025T194218Z.xml diff --git a/src/main/java/org/gridsuite/modification/server/ModificationType.java b/src/main/java/org/gridsuite/modification/server/ModificationType.java index 85a2b0ebe..ad0479451 100644 --- a/src/main/java/org/gridsuite/modification/server/ModificationType.java +++ b/src/main/java/org/gridsuite/modification/server/ModificationType.java @@ -45,7 +45,8 @@ public enum ModificationType { GENERATION_DISPATCH(PreloadingStrategy.COLLECTION), VOLTAGE_INIT_MODIFICATION(PreloadingStrategy.COLLECTION), VSC_CREATION(PreloadingStrategy.NONE), - CONVERTER_STATION_CREATION(PreloadingStrategy.NONE); + CONVERTER_STATION_CREATION(PreloadingStrategy.NONE), + BY_FORMULA_MODIFICATION(PreloadingStrategy.COLLECTION); private final PreloadingStrategy strategy; diff --git a/src/main/java/org/gridsuite/modification/server/NetworkModificationException.java b/src/main/java/org/gridsuite/modification/server/NetworkModificationException.java index c6296dd0e..6d013a7f4 100644 --- a/src/main/java/org/gridsuite/modification/server/NetworkModificationException.java +++ b/src/main/java/org/gridsuite/modification/server/NetworkModificationException.java @@ -102,7 +102,8 @@ public enum Type { CREATE_VSC_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), HVDC_LINE_ALREADY_EXISTS(HttpStatus.BAD_REQUEST), VSC_CONVERTER_STATION_NOT_FOUND(HttpStatus.NOT_FOUND), - CREATE_CONVERTER_STATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR); + CREATE_CONVERTER_STATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + BY_FORMULA_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR); public final HttpStatus status; private final String message; diff --git a/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java index 834942f72..00e4ad3e9 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java @@ -1,13 +1,17 @@ package org.gridsuite.modification.server.dto; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.powsybl.iidm.network.IdentifiableType; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.gridsuite.modification.server.dto.annotation.ModificationErrorTypeName; import org.gridsuite.modification.server.dto.formula.FormulaInfos; import org.gridsuite.modification.server.entities.equipment.modification.ByFormulaModificationEntity; +import org.gridsuite.modification.server.modifications.ByFormulaModification; import java.util.List; @@ -15,6 +19,8 @@ @AllArgsConstructor @Getter @Setter +@JsonTypeName("BY_FORMULA_MODIFICATION") +@ModificationErrorTypeName("BY_FORMULA_MODIFICATION_ERROR") public class ByFormulaModificationInfos extends ModificationInfos { @Schema(description = "Identifiable type") private IdentifiableType identifiableType; @@ -26,4 +32,9 @@ public class ByFormulaModificationInfos extends ModificationInfos { public ByFormulaModificationEntity toEntity() { return new ByFormulaModificationEntity(this); } + + @Override + public ByFormulaModification toModification() { + return new ByFormulaModification(this); + } } diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/BatteryField.java b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/BatteryField.java index 8d41c04c8..aba3d79d5 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/BatteryField.java +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/BatteryField.java @@ -3,9 +3,17 @@ import com.powsybl.iidm.network.Battery; import com.powsybl.iidm.network.Generator; import com.powsybl.iidm.network.IdentifiableType; +import com.powsybl.iidm.network.MinMaxReactiveLimits; +import com.powsybl.iidm.network.extensions.ActivePowerControl; +import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; +import org.gridsuite.modification.server.NetworkModificationException; public enum BatteryField implements EquipmentField { - ACTIVE_POWER; + MINIMUM_ACTIVE_POWER, + MAXIMUM_ACTIVE_POWER, + ACTIVE_POWER_SET_POINT, + REACTIVE_POWER_SET_POINT, + DROOP; @Override public IdentifiableType getIdentifiableType() { @@ -13,8 +21,25 @@ public IdentifiableType getIdentifiableType() { } public static Double getReferenceValue(Battery battery, BatteryField batteryField) { + ActivePowerControl activePowerControl = battery.getExtension(ActivePowerControl.class); return switch (batteryField) { - case ACTIVE_POWER -> battery.getMaxP(); + case MINIMUM_ACTIVE_POWER -> battery.getMinP(); + case MAXIMUM_ACTIVE_POWER -> battery.getMaxP(); + case ACTIVE_POWER_SET_POINT -> battery.getTargetP(); + case REACTIVE_POWER_SET_POINT -> battery.getTargetQ(); + case DROOP -> activePowerControl != null ? activePowerControl.getDroop() : null; }; } + + public static void setNewValue(Battery battery, BatteryField batteryField, Double newValue) { + switch (batteryField) { + case MINIMUM_ACTIVE_POWER -> battery.setMinP(newValue); + case MAXIMUM_ACTIVE_POWER -> battery.setMaxP(newValue); + case ACTIVE_POWER_SET_POINT -> battery.setTargetP(newValue); + case REACTIVE_POWER_SET_POINT -> battery.setTargetQ(newValue); + case DROOP -> battery.newExtension(ActivePowerControlAdder.class) + .withDroop(newValue) + .add(); + } + } } diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java index 8f3bc71cb..6108e168c 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java @@ -20,12 +20,10 @@ public enum GeneratorField implements EquipmentField { ACTIVE_POWER_SET_POINT, REACTIVE_POWER_SET_POINT, VOLTAGE_SET_POINT, - PLANNING_ACTIVE_POWER_SET_POINT, + PLANNED_ACTIVE_POWER_SET_POINT, MARGINAL_COST, - PLANNING_OUTAGE_RATE, + PLANNED_OUTAGE_RATE, FORCED_OUTAGE_RATE, - MINIMUM_REACTIVE_POWER, - MAXIMUM_REACTIVE_POWER, DROOP, TRANSIENT_REACTANCE, STEP_UP_TRANSFORMER_REACTANCE, @@ -37,7 +35,6 @@ public IdentifiableType getIdentifiableType() { } public static Double getReferenceValue(Generator generator, GeneratorField generatorField) { - MinMaxReactiveLimits minMaxReactiveLimits = generator.getReactiveLimits(MinMaxReactiveLimits.class); ActivePowerControl activePowerControl = generator.getExtension(ActivePowerControl.class); GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); GeneratorShortCircuit generatorShortCircuit = generator.getExtension(GeneratorShortCircuit.class); @@ -49,12 +46,10 @@ public static Double getReferenceValue(Generator generator, GeneratorField gener case RATED_NOMINAL_POWER -> generator.getRatedS(); case REACTIVE_POWER_SET_POINT -> generator.getTargetQ(); case VOLTAGE_SET_POINT -> generator.getTargetV(); - case PLANNING_ACTIVE_POWER_SET_POINT -> generatorStartup != null ? generatorStartup.getPlannedActivePowerSetpoint() : null; + case PLANNED_ACTIVE_POWER_SET_POINT -> generatorStartup != null ? generatorStartup.getPlannedActivePowerSetpoint() : null; case MARGINAL_COST -> generatorStartup != null ? generatorStartup.getMarginalCost() : null; - case PLANNING_OUTAGE_RATE -> generatorStartup != null ? generatorStartup.getPlannedOutageRate() : null; + case PLANNED_OUTAGE_RATE -> generatorStartup != null ? generatorStartup.getPlannedOutageRate() : null; case FORCED_OUTAGE_RATE -> generatorStartup != null ? generatorStartup.getForcedOutageRate() : null; - case MINIMUM_REACTIVE_POWER -> minMaxReactiveLimits != null ? minMaxReactiveLimits.getMinQ() : null; - case MAXIMUM_REACTIVE_POWER -> minMaxReactiveLimits != null ? minMaxReactiveLimits.getMaxQ() : null; case DROOP -> activePowerControl != null ? activePowerControl.getDroop() : null; case TRANSIENT_REACTANCE -> generatorShortCircuit != null ? generatorShortCircuit.getDirectTransX() : null; case STEP_UP_TRANSFORMER_REACTANCE -> generatorShortCircuit != null ? generatorShortCircuit.getStepUpTransformerX() : null; @@ -66,8 +61,6 @@ public static void setNewValue(Generator generator, GeneratorField generatorFiel GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); GeneratorShortCircuit generatorShortCircuit = generator.getExtension(GeneratorShortCircuit.class); MinMaxReactiveLimits minMaxReactiveLimits = generator.getReactiveLimits(MinMaxReactiveLimits.class); - ActivePowerControl activePowerControl = generator.getExtension(ActivePowerControl.class); - CoordinatedReactiveControl coordinatedReactiveControl = generator.getExtension(CoordinatedReactiveControl.class); switch (generatorField) { case MAXIMUM_ACTIVE_POWER -> generator.setMaxP(newValue); case MINIMUM_ACTIVE_POWER -> generator.setMinP(newValue); @@ -75,13 +68,18 @@ public static void setNewValue(Generator generator, GeneratorField generatorFiel case RATED_NOMINAL_POWER -> generator.setRatedS(newValue); case REACTIVE_POWER_SET_POINT -> generator.setTargetQ(newValue); case VOLTAGE_SET_POINT -> generator.setTargetV(newValue); - case PLANNING_ACTIVE_POWER_SET_POINT -> { + case PLANNED_ACTIVE_POWER_SET_POINT -> { if (generatorStartup == null) { generator.newExtension(GeneratorStartupAdder.class) .withPlannedActivePowerSetpoint(newValue) .add(); } else { - generatorStartup.setPlannedActivePowerSetpoint(newValue); + generator.newExtension(GeneratorStartupAdder.class) + .withMarginalCost(generatorStartup.getMarginalCost()) + .withPlannedActivePowerSetpoint(newValue) + .withPlannedOutageRate(generatorStartup.getPlannedOutageRate()) + .withForcedOutageRate(generatorStartup.getForcedOutageRate()) + .add(); } } case MARGINAL_COST -> { @@ -90,16 +88,26 @@ public static void setNewValue(Generator generator, GeneratorField generatorFiel .withMarginalCost(newValue) .add(); } else { - generatorStartup.setMarginalCost(newValue); + generator.newExtension(GeneratorStartupAdder.class) + .withMarginalCost(newValue) + .withPlannedActivePowerSetpoint(generatorStartup.getPlannedActivePowerSetpoint()) + .withPlannedOutageRate(generatorStartup.getPlannedOutageRate()) + .withForcedOutageRate(generatorStartup.getForcedOutageRate()) + .add(); } } - case PLANNING_OUTAGE_RATE -> { + case PLANNED_OUTAGE_RATE -> { if (generatorStartup == null) { generator.newExtension(GeneratorStartupAdder.class) .withPlannedOutageRate(newValue) .add(); } else { - generatorStartup.setPlannedOutageRate(newValue); + generator.newExtension(GeneratorStartupAdder.class) + .withMarginalCost(generatorStartup.getMarginalCost()) + .withPlannedActivePowerSetpoint(generatorStartup.getPlannedActivePowerSetpoint()) + .withPlannedOutageRate(newValue) + .withForcedOutageRate(generatorStartup.getForcedOutageRate()) + .add(); } } case FORCED_OUTAGE_RATE -> { @@ -108,36 +116,17 @@ public static void setNewValue(Generator generator, GeneratorField generatorFiel .withForcedOutageRate(newValue) .add(); } else { - generatorStartup.setForcedOutageRate(newValue); - } - } - case MINIMUM_REACTIVE_POWER -> { - if (minMaxReactiveLimits ==null) { - throw new NetworkModificationException(NetworkModificationException.Type.MODIFY_GENERATOR_ERROR, "TODO"); - } - generator.newMinMaxReactiveLimits() - .setMinQ(newValue) - .setMaxQ(minMaxReactiveLimits.getMaxQ()) - .add(); - } - case MAXIMUM_REACTIVE_POWER -> { - if (minMaxReactiveLimits ==null) { - throw new NetworkModificationException(NetworkModificationException.Type.MODIFY_GENERATOR_ERROR, "TODO"); - } - generator.newMinMaxReactiveLimits() - .setMaxQ(newValue) - .setMinQ(minMaxReactiveLimits.getMinQ()) - .add(); - } - case DROOP -> { - if (activePowerControl == null) { - generator.newExtension(ActivePowerControlAdder.class) - .withDroop(newValue) + generator.newExtension(GeneratorStartupAdder.class) + .withMarginalCost(generatorStartup.getMarginalCost()) + .withPlannedActivePowerSetpoint(generatorStartup.getPlannedActivePowerSetpoint()) + .withPlannedOutageRate(generatorStartup.getForcedOutageRate()) + .withForcedOutageRate(newValue) .add(); - } else { - activePowerControl.setDroop(newValue); } } + case DROOP -> generator.newExtension(ActivePowerControlAdder.class) + .withDroop(newValue) + .add(); case TRANSIENT_REACTANCE -> generator.newExtension(GeneratorShortCircuitAdder.class) .withDirectTransX(newValue) .withStepUpTransformerX(generatorShortCircuit == null ? Double.NaN : generatorShortCircuit.getStepUpTransformerX()) diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java index 8370a8e0b..65ec899d1 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java @@ -44,6 +44,7 @@ public class FormulaEntity { private Operator operator; public FormulaEntity(FormulaInfos formulaInfos) { + this.id = UUID.randomUUID(); this.filters = formulaInfos.getFilters().stream().map(FilterInfos::toEntity).collect(Collectors.toList()); this.equipmentField1 = formulaInfos.getFieldOrValue1().getEquipmentField().toString(); this.equipmentField2 = formulaInfos.getFieldOrValue2().getEquipmentField().toString(); 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 3dc427b1a..673abc432 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -2,15 +2,18 @@ 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.Network; import com.powsybl.network.store.iidm.impl.NetworkImpl; +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.service.FilterService; import org.springframework.util.CollectionUtils; @@ -88,11 +91,15 @@ private void applyFormula(Network network, String identifiableId, FormulaInfos f Double value1 = formulaInfos.getFieldOrValue1().getRefOrValue(identifiable); Double value2 = formulaInfos.getFieldOrValue2().getRefOrValue(identifiable); switch (identifiable.getType()) { - case GENERATOR -> { - GeneratorField.setNewValue((Generator) identifiable, - (GeneratorField) formulaInfos.getEquipmentField(), + case GENERATOR -> GeneratorField.setNewValue((Generator) identifiable, + (GeneratorField) formulaInfos.getEquipmentField(), + applyOperation(formulaInfos.getOperator(), value1, value2)); + case BATTERY -> { + BatteryField.setNewValue((Battery) identifiable, + (BatteryField) formulaInfos.getEquipmentField(), applyOperation(formulaInfos.getOperator(), value1, value2)); } + default -> throw new NetworkModificationException(NetworkModificationException.Type.WRONG_EQUIPMENT_TYPE, "Unsupported equipment"); } } diff --git a/src/main/resources/db/changelog/changesets/changelog_20231025T194218Z.xml b/src/main/resources/db/changelog/changesets/changelog_20231025T194218Z.xml new file mode 100644 index 000000000..c0923bd9d --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20231025T194218Z.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 00f250fa0..ade83dd26 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -201,3 +201,6 @@ databaseChangeLog: - include: file: changesets/changelog_20230801T194601Z.xml relativeToChangelogFile: true + - include: + file: changesets/changelog_20231025T194218Z.xml + relativeToChangelogFile: true From 7170362278d70c189708cd7c91d73c0c9132d4b6 Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Mon, 30 Oct 2023 12:23:20 +0100 Subject: [PATCH 03/20] add unit tests --- .../dto/ByFormulaModificationInfos.java | 23 +- .../server/dto/ModificationInfos.java | 3 +- .../server/dto/formula/FormulaInfos.java | 24 +- .../server/dto/formula/Operator.java | 11 + .../dto/formula/ReferenceFieldOrValue.java | 40 +- .../formula/equipmentfield/BatteryField.java | 32 +- .../equipmentfield/EquipmentField.java | 7 - .../equipmentfield/GeneratorField.java | 182 +++---- .../ByFormulaModificationEntity.java | 28 +- .../equipment/modification/FormulaEntity.java | 27 +- .../modifications/ByFormulaModification.java | 21 +- ...18Z.xml => changelog_20231026T111001Z.xml} | 23 +- .../db/changelog/db.changelog-master.yaml | 2 +- .../AbstractByFormulaModificationTest.java | 241 ++++++++++ .../BatteryByFormulaModificationTest.java | 242 ++++++++++ .../GeneratorByFormulaModificationTest.java | 447 ++++++++++++++++++ 16 files changed, 1201 insertions(+), 152 deletions(-) delete mode 100644 src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/EquipmentField.java rename src/main/resources/db/changelog/changesets/{changelog_20231025T194218Z.xml => changelog_20231026T111001Z.xml} (84%) create mode 100644 src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java create mode 100644 src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java create mode 100644 src/test/java/org/gridsuite/modification/server/modifications/GeneratorByFormulaModificationTest.java diff --git a/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java index 00e4ad3e9..26d20a166 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java @@ -1,13 +1,23 @@ +/** + * 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.dto; -import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeName; +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.commons.reporter.ReporterModel; import com.powsybl.iidm.network.IdentifiableType; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.server.ModificationType; import org.gridsuite.modification.server.dto.annotation.ModificationErrorTypeName; import org.gridsuite.modification.server.dto.formula.FormulaInfos; import org.gridsuite.modification.server.entities.equipment.modification.ByFormulaModificationEntity; @@ -15,12 +25,18 @@ import java.util.List; +/** + * @author Seddik Yengui + */ + +@SuperBuilder @NoArgsConstructor @AllArgsConstructor @Getter @Setter @JsonTypeName("BY_FORMULA_MODIFICATION") @ModificationErrorTypeName("BY_FORMULA_MODIFICATION_ERROR") +@Schema(description = "Modification by formula") public class ByFormulaModificationInfos extends ModificationInfos { @Schema(description = "Identifiable type") private IdentifiableType identifiableType; @@ -37,4 +53,9 @@ public ByFormulaModificationEntity toEntity() { public ByFormulaModification toModification() { return new ByFormulaModification(this); } + + @Override + public Reporter createSubReporter(ReporterModel reporter) { + return reporter.createSubReporter(ModificationType.BY_FORMULA_MODIFICATION.name(), "By formula modification"); + } } diff --git a/src/main/java/org/gridsuite/modification/server/dto/ModificationInfos.java b/src/main/java/org/gridsuite/modification/server/dto/ModificationInfos.java index f29291663..f5fe6f98f 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/ModificationInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/ModificationInfos.java @@ -65,7 +65,8 @@ @JsonSubTypes.Type(value = GenerationDispatchInfos.class), @JsonSubTypes.Type(value = VoltageInitModificationInfos.class), @JsonSubTypes.Type(value = VscCreationInfos.class), - @JsonSubTypes.Type(value = ConverterStationCreationInfos.class) + @JsonSubTypes.Type(value = ConverterStationCreationInfos.class), + @JsonSubTypes.Type(value = ByFormulaModificationInfos.class) }) @SuperBuilder @NoArgsConstructor diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java b/src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java index 7ada7b26f..65079e515 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java @@ -1,3 +1,10 @@ +/** + * 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.dto.formula; import io.swagger.v3.oas.annotations.media.Schema; @@ -5,12 +12,17 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.gridsuite.modification.server.dto.FilterEquipments; +import lombok.experimental.SuperBuilder; import org.gridsuite.modification.server.dto.FilterInfos; -import org.gridsuite.modification.server.dto.formula.equipmentfield.EquipmentField; +import org.gridsuite.modification.server.entities.equipment.modification.FormulaEntity; import java.util.List; +/** + * @author Seddik Yengui + */ + +@SuperBuilder @NoArgsConstructor @AllArgsConstructor @Getter @@ -19,8 +31,8 @@ public class FormulaInfos { @Schema(description = "List of filters") private List filters; - @Schema(description = "Equipment field") - private EquipmentField equipmentField; + @Schema(description = "Edited field") + private String editedField; @Schema(description = "First reference field or value") private ReferenceFieldOrValue fieldOrValue1; @@ -30,4 +42,8 @@ public class FormulaInfos { @Schema(description = "Operator") private Operator operator; + + public FormulaEntity toEntity() { + return new FormulaEntity(this); + } } diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java b/src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java index 55f926622..e2ecf2926 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java @@ -1,5 +1,16 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + package org.gridsuite.modification.server.dto.formula; +/** + * @author Seddik Yengui + */ + public enum Operator { ADDITION, SUBTRACTION, 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 be05a12d6..018d3fe9b 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 @@ -1,3 +1,10 @@ +/** + * 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.dto.formula; import com.powsybl.iidm.network.Battery; @@ -5,32 +12,51 @@ import com.powsybl.iidm.network.Identifiable; import com.powsybl.iidm.network.IdentifiableType; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.gridsuite.modification.server.NetworkModificationException; import org.gridsuite.modification.server.dto.formula.equipmentfield.BatteryField; -import org.gridsuite.modification.server.dto.formula.equipmentfield.EquipmentField; import org.gridsuite.modification.server.dto.formula.equipmentfield.GeneratorField; +/** + * @author Seddik Yengui + */ + +@Builder @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class ReferenceFieldOrValue { - private EquipmentField equipmentField; + private String equipmentField; private Double value; public Double getRefOrValue(Identifiable identifiable) { - if (value != null) { + if (value == null && equipmentField == null) { + throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, + "There is no value or reference to any of the equipment fields"); + } + + if (value != null && !Double.isNaN(value)) { return value; } IdentifiableType identifiableType = identifiable.getType(); - return switch (identifiableType) { - case GENERATOR -> GeneratorField.getReferenceValue((Generator) identifiable, (GeneratorField) equipmentField); - case BATTERY -> BatteryField.getReferenceValue((Battery) identifiable, (BatteryField) equipmentField); - default -> throw new UnsupportedOperationException("TODO"); + Double referenceValue = switch (identifiableType) { + case GENERATOR -> GeneratorField.getReferenceValue((Generator) identifiable, equipmentField); + case BATTERY -> BatteryField.getReferenceValue((Battery) 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/BatteryField.java b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/BatteryField.java index aba3d79d5..079dd8a12 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/BatteryField.java +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/BatteryField.java @@ -1,28 +1,31 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + package org.gridsuite.modification.server.dto.formula.equipmentfield; import com.powsybl.iidm.network.Battery; -import com.powsybl.iidm.network.Generator; -import com.powsybl.iidm.network.IdentifiableType; -import com.powsybl.iidm.network.MinMaxReactiveLimits; import com.powsybl.iidm.network.extensions.ActivePowerControl; import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; -import org.gridsuite.modification.server.NetworkModificationException; -public enum BatteryField implements EquipmentField { +/** + * @author Seddik Yengui + */ + +public enum BatteryField { MINIMUM_ACTIVE_POWER, MAXIMUM_ACTIVE_POWER, ACTIVE_POWER_SET_POINT, REACTIVE_POWER_SET_POINT, DROOP; - @Override - public IdentifiableType getIdentifiableType() { - return IdentifiableType.BATTERY; - } - - public static Double getReferenceValue(Battery battery, BatteryField batteryField) { + public static Double getReferenceValue(Battery battery, String batteryField) { ActivePowerControl activePowerControl = battery.getExtension(ActivePowerControl.class); - return switch (batteryField) { + BatteryField field = BatteryField.valueOf(batteryField); + return switch (field) { case MINIMUM_ACTIVE_POWER -> battery.getMinP(); case MAXIMUM_ACTIVE_POWER -> battery.getMaxP(); case ACTIVE_POWER_SET_POINT -> battery.getTargetP(); @@ -31,8 +34,9 @@ public static Double getReferenceValue(Battery battery, BatteryField batteryFiel }; } - public static void setNewValue(Battery battery, BatteryField batteryField, Double newValue) { - switch (batteryField) { + public static void setNewValue(Battery battery, String batteryField, Double newValue) { + BatteryField field = BatteryField.valueOf(batteryField); + switch (field) { case MINIMUM_ACTIVE_POWER -> battery.setMinP(newValue); case MAXIMUM_ACTIVE_POWER -> battery.setMaxP(newValue); case ACTIVE_POWER_SET_POINT -> battery.setTargetP(newValue); diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/EquipmentField.java b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/EquipmentField.java deleted file mode 100644 index 8de47ef8b..000000000 --- a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/EquipmentField.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.gridsuite.modification.server.dto.formula.equipmentfield; - -import com.powsybl.iidm.network.IdentifiableType; - -public interface EquipmentField { - IdentifiableType getIdentifiableType(); -} diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java index 6108e168c..7c864cbfd 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/equipmentfield/GeneratorField.java @@ -1,8 +1,13 @@ +/** + * 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.dto.formula.equipmentfield; import com.powsybl.iidm.network.Generator; -import com.powsybl.iidm.network.IdentifiableType; -import com.powsybl.iidm.network.MinMaxReactiveLimits; import com.powsybl.iidm.network.extensions.ActivePowerControl; import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; import com.powsybl.iidm.network.extensions.CoordinatedReactiveControl; @@ -11,9 +16,12 @@ import com.powsybl.iidm.network.extensions.GeneratorShortCircuitAdder; import com.powsybl.iidm.network.extensions.GeneratorStartup; import com.powsybl.iidm.network.extensions.GeneratorStartupAdder; -import org.gridsuite.modification.server.NetworkModificationException; -public enum GeneratorField implements EquipmentField { +/** + * @author Seddik Yengui + */ + +public enum GeneratorField { MINIMUM_ACTIVE_POWER, MAXIMUM_ACTIVE_POWER, RATED_NOMINAL_POWER, @@ -29,17 +37,13 @@ public enum GeneratorField implements EquipmentField { STEP_UP_TRANSFORMER_REACTANCE, Q_PERCENT; - @Override - public IdentifiableType getIdentifiableType() { - return IdentifiableType.GENERATOR; - } - - public static Double getReferenceValue(Generator generator, GeneratorField generatorField) { + public static Double getReferenceValue(Generator generator, String generatorField) { ActivePowerControl activePowerControl = generator.getExtension(ActivePowerControl.class); GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); GeneratorShortCircuit generatorShortCircuit = generator.getExtension(GeneratorShortCircuit.class); CoordinatedReactiveControl coordinatedReactiveControl = generator.getExtension(CoordinatedReactiveControl.class); - return switch (generatorField) { + GeneratorField field = GeneratorField.valueOf(generatorField); + return switch (field) { case MAXIMUM_ACTIVE_POWER -> generator.getMaxP(); case MINIMUM_ACTIVE_POWER -> generator.getMinP(); case ACTIVE_POWER_SET_POINT -> generator.getTargetP(); @@ -57,87 +61,89 @@ public static Double getReferenceValue(Generator generator, GeneratorField gener }; } - public static void setNewValue(Generator generator, GeneratorField generatorField, Double newValue) { - GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); - GeneratorShortCircuit generatorShortCircuit = generator.getExtension(GeneratorShortCircuit.class); - MinMaxReactiveLimits minMaxReactiveLimits = generator.getReactiveLimits(MinMaxReactiveLimits.class); - switch (generatorField) { - case MAXIMUM_ACTIVE_POWER -> generator.setMaxP(newValue); - case MINIMUM_ACTIVE_POWER -> generator.setMinP(newValue); - case ACTIVE_POWER_SET_POINT -> generator.setTargetP(newValue); - case RATED_NOMINAL_POWER -> generator.setRatedS(newValue); - case REACTIVE_POWER_SET_POINT -> generator.setTargetQ(newValue); - case VOLTAGE_SET_POINT -> generator.setTargetV(newValue); - case PLANNED_ACTIVE_POWER_SET_POINT -> { - if (generatorStartup == null) { - generator.newExtension(GeneratorStartupAdder.class) - .withPlannedActivePowerSetpoint(newValue) - .add(); - } else { - generator.newExtension(GeneratorStartupAdder.class) - .withMarginalCost(generatorStartup.getMarginalCost()) - .withPlannedActivePowerSetpoint(newValue) - .withPlannedOutageRate(generatorStartup.getPlannedOutageRate()) - .withForcedOutageRate(generatorStartup.getForcedOutageRate()) - .add(); + public static void setNewValue(Generator generator, String generatorField, Double newValue) { + if (!Double.isNaN(newValue)) { + GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); + GeneratorShortCircuit generatorShortCircuit = generator.getExtension(GeneratorShortCircuit.class); + GeneratorField field = GeneratorField.valueOf(generatorField); + switch (field) { + case MAXIMUM_ACTIVE_POWER -> generator.setMaxP(newValue); + case MINIMUM_ACTIVE_POWER -> generator.setMinP(newValue); + case ACTIVE_POWER_SET_POINT -> generator.setTargetP(newValue); + case RATED_NOMINAL_POWER -> generator.setRatedS(newValue); + case REACTIVE_POWER_SET_POINT -> generator.setTargetQ(newValue); + case VOLTAGE_SET_POINT -> generator.setTargetV(newValue); + case PLANNED_ACTIVE_POWER_SET_POINT -> { + if (generatorStartup == null) { + generator.newExtension(GeneratorStartupAdder.class) + .withPlannedActivePowerSetpoint(newValue) + .add(); + } else { + generator.newExtension(GeneratorStartupAdder.class) + .withMarginalCost(generatorStartup.getMarginalCost()) + .withPlannedActivePowerSetpoint(newValue) + .withPlannedOutageRate(generatorStartup.getPlannedOutageRate()) + .withForcedOutageRate(generatorStartup.getForcedOutageRate()) + .add(); + } } - } - case MARGINAL_COST -> { - if (generatorStartup == null) { - generator.newExtension(GeneratorStartupAdder.class) - .withMarginalCost(newValue) - .add(); - } else { - generator.newExtension(GeneratorStartupAdder.class) - .withMarginalCost(newValue) - .withPlannedActivePowerSetpoint(generatorStartup.getPlannedActivePowerSetpoint()) - .withPlannedOutageRate(generatorStartup.getPlannedOutageRate()) - .withForcedOutageRate(generatorStartup.getForcedOutageRate()) - .add(); + case MARGINAL_COST -> { + if (generatorStartup == null) { + generator.newExtension(GeneratorStartupAdder.class) + .withMarginalCost(newValue) + .add(); + } else { + generator.newExtension(GeneratorStartupAdder.class) + .withMarginalCost(newValue) + .withPlannedActivePowerSetpoint(generatorStartup.getPlannedActivePowerSetpoint()) + .withPlannedOutageRate(generatorStartup.getPlannedOutageRate()) + .withForcedOutageRate(generatorStartup.getForcedOutageRate()) + .add(); + } } - } - case PLANNED_OUTAGE_RATE -> { - if (generatorStartup == null) { - generator.newExtension(GeneratorStartupAdder.class) - .withPlannedOutageRate(newValue) - .add(); - } else { - generator.newExtension(GeneratorStartupAdder.class) - .withMarginalCost(generatorStartup.getMarginalCost()) - .withPlannedActivePowerSetpoint(generatorStartup.getPlannedActivePowerSetpoint()) - .withPlannedOutageRate(newValue) - .withForcedOutageRate(generatorStartup.getForcedOutageRate()) - .add(); + case PLANNED_OUTAGE_RATE -> { + if (generatorStartup == null) { + generator.newExtension(GeneratorStartupAdder.class) + .withPlannedOutageRate(newValue) + .add(); + } else { + generator.newExtension(GeneratorStartupAdder.class) + .withMarginalCost(generatorStartup.getMarginalCost()) + .withPlannedActivePowerSetpoint(generatorStartup.getPlannedActivePowerSetpoint()) + .withPlannedOutageRate(newValue) + .withForcedOutageRate(generatorStartup.getForcedOutageRate()) + .add(); + } } - } - case FORCED_OUTAGE_RATE -> { - if (generatorStartup == null) { - generator.newExtension(GeneratorStartupAdder.class) - .withForcedOutageRate(newValue) - .add(); - } else { - generator.newExtension(GeneratorStartupAdder.class) - .withMarginalCost(generatorStartup.getMarginalCost()) - .withPlannedActivePowerSetpoint(generatorStartup.getPlannedActivePowerSetpoint()) - .withPlannedOutageRate(generatorStartup.getForcedOutageRate()) - .withForcedOutageRate(newValue) - .add(); + case FORCED_OUTAGE_RATE -> { + if (generatorStartup == null) { + generator.newExtension(GeneratorStartupAdder.class) + .withForcedOutageRate(newValue) + .add(); + } else { + generator.newExtension(GeneratorStartupAdder.class) + .withMarginalCost(generatorStartup.getMarginalCost()) + .withPlannedActivePowerSetpoint(generatorStartup.getPlannedActivePowerSetpoint()) + .withPlannedOutageRate(generatorStartup.getForcedOutageRate()) + .withForcedOutageRate(newValue) + .add(); + } } + case DROOP -> generator.newExtension(ActivePowerControlAdder.class) + .withDroop(newValue) + .add(); + case TRANSIENT_REACTANCE -> generator.newExtension(GeneratorShortCircuitAdder.class) + .withDirectTransX(newValue) + .withStepUpTransformerX(generatorShortCircuit == null ? Double.NaN : generatorShortCircuit.getStepUpTransformerX()) + .add(); + case STEP_UP_TRANSFORMER_REACTANCE -> generator.newExtension(GeneratorShortCircuitAdder.class) + .withDirectTransX(generatorShortCircuit == null ? 0.0D : generatorShortCircuit.getDirectTransX()) + .withStepUpTransformerX(newValue) + .add(); + case Q_PERCENT -> generator.newExtension(CoordinatedReactiveControlAdder.class) + .withQPercent(newValue) + .add(); } - case DROOP -> generator.newExtension(ActivePowerControlAdder.class) - .withDroop(newValue) - .add(); - case TRANSIENT_REACTANCE -> generator.newExtension(GeneratorShortCircuitAdder.class) - .withDirectTransX(newValue) - .withStepUpTransformerX(generatorShortCircuit == null ? Double.NaN : generatorShortCircuit.getStepUpTransformerX()) - .add(); - case STEP_UP_TRANSFORMER_REACTANCE -> generator.newExtension(GeneratorShortCircuitAdder.class) - .withDirectTransX(generatorShortCircuit == null ? 0.0D : generatorShortCircuit.getDirectTransX()) - .withStepUpTransformerX(newValue) - .add(); - case Q_PERCENT -> generator.newExtension(CoordinatedReactiveControlAdder.class) - .withQPercent(newValue) - .add(); } } -} \ No newline at end of file +} diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java index aab1ebfe0..fee797da7 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java @@ -12,6 +12,7 @@ import lombok.NonNull; import org.gridsuite.modification.server.dto.ByFormulaModificationInfos; import org.gridsuite.modification.server.dto.ModificationInfos; +import org.gridsuite.modification.server.dto.formula.FormulaInfos; import org.gridsuite.modification.server.entities.ModificationEntity; import java.util.List; @@ -33,6 +34,7 @@ public ByFormulaModificationEntity(ByFormulaModificationInfos byFormulaModificat assignAttributes(byFormulaModificationInfos); } + @Override public void update(@NonNull ModificationInfos modificationInfos) { super.update(modificationInfos); assignAttributes((ByFormulaModificationInfos) modificationInfos); @@ -40,8 +42,28 @@ public void update(@NonNull ModificationInfos modificationInfos) { private void assignAttributes(ByFormulaModificationInfos byFormulaModificationInfos) { this.identifiableType = byFormulaModificationInfos.getIdentifiableType(); - this.formulaEntities = byFormulaModificationInfos.getFormulaInfosList().stream() - .map(FormulaEntity::new) - .collect(Collectors.toList()); + List formulas = byFormulaModificationInfos.getFormulaInfosList().stream() + .map(FormulaInfos::toEntity).toList(); + if (formulaEntities == null) { + formulaEntities = formulas; + } else { + formulaEntities.clear(); + formulaEntities.addAll(formulas); + } + } + + @Override + public ByFormulaModificationInfos toModificationInfos() { + return toByFormulaModificationInfosBuilder().build(); + } + + private ByFormulaModificationInfos.ByFormulaModificationInfosBuilder toByFormulaModificationInfosBuilder() { + return ByFormulaModificationInfos.builder() + .uuid(getId()) + .date(getDate()) + .identifiableType(getIdentifiableType()) + .formulaInfosList(getFormulaEntities().stream() + .map(FormulaEntity::toFormulaInfos) + .collect(Collectors.toList())); } } diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java index 65ec899d1..2ab805aa5 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java @@ -6,7 +6,7 @@ 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.EquipmentField; +import org.gridsuite.modification.server.dto.formula.ReferenceFieldOrValue; import java.util.List; import java.util.UUID; @@ -28,6 +28,9 @@ public class FormulaEntity { inverseJoinColumns = @JoinColumn(name = "filterId")) private List filters; + @Column + private String editedField; + @Column private String equipmentField1; @@ -44,12 +47,28 @@ public class FormulaEntity { private Operator operator; public FormulaEntity(FormulaInfos formulaInfos) { - this.id = UUID.randomUUID(); this.filters = formulaInfos.getFilters().stream().map(FilterInfos::toEntity).collect(Collectors.toList()); - this.equipmentField1 = formulaInfos.getFieldOrValue1().getEquipmentField().toString(); - this.equipmentField2 = formulaInfos.getFieldOrValue2().getEquipmentField().toString(); + this.editedField = formulaInfos.getEditedField(); + this.equipmentField1 = formulaInfos.getFieldOrValue1().getEquipmentField(); + this.equipmentField2 = formulaInfos.getFieldOrValue2().getEquipmentField(); this.value1 = formulaInfos.getFieldOrValue1().getValue(); this.value2 = formulaInfos.getFieldOrValue2().getValue(); this.operator = formulaInfos.getOperator(); } + + public FormulaInfos toFormulaInfos() { + return FormulaInfos.builder() + .filters(getFilters().stream() + .map(filterEntity -> new FilterInfos(filterEntity.getFilterId(), filterEntity.getName())) + .collect(Collectors.toList())) + .editedField(getEditedField()) + .fieldOrValue1(ReferenceFieldOrValue.builder() + .equipmentField(getEquipmentField1()).value(getValue1()) + .build()) + .fieldOrValue2(ReferenceFieldOrValue.builder() + .equipmentField(getEquipmentField2()).value(getValue2()) + .build()) + .operator(getOperator()) + .build(); + } } 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 673abc432..284e0b38f 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -27,7 +27,7 @@ import static org.gridsuite.modification.server.modifications.ModificationUtils.createReport; import static org.gridsuite.modification.server.modifications.ModificationUtils.distinctByKey; -public class ByFormulaModification extends AbstractModification { +public class ByFormulaModification extends AbstractModification { private ByFormulaModificationInfos modificationInfos; protected FilterService filterService; @@ -80,25 +80,24 @@ public void apply(Network network, Reporter subReporter) { TypedValue.WARN_SEVERITY); }); - modificationInfos.getFormulaInfosList().forEach(formulaInfos -> formulaInfos.getFilters().forEach((filterInfos -> { + modificationInfos.getFormulaInfosList().forEach(formulaInfos -> formulaInfos.getFilters().forEach(filterInfos -> { var filterEquipments = exportFilters.get(filterInfos.getId()); filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, attributes.getId(), formulaInfos)); - }))); + })); } private void applyFormula(Network network, String identifiableId, FormulaInfos formulaInfos) { Identifiable identifiable = network.getIdentifiable(identifiableId); 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, - (GeneratorField) formulaInfos.getEquipmentField(), - applyOperation(formulaInfos.getOperator(), value1, value2)); - case BATTERY -> { - BatteryField.setNewValue((Battery) identifiable, - (BatteryField) formulaInfos.getEquipmentField(), - applyOperation(formulaInfos.getOperator(), value1, value2)); - } + formulaInfos.getEditedField(), + newValue); + case BATTERY -> BatteryField.setNewValue((Battery) identifiable, + formulaInfos.getEditedField(), + newValue); default -> throw new NetworkModificationException(NetworkModificationException.Type.WRONG_EQUIPMENT_TYPE, "Unsupported equipment"); } } @@ -106,7 +105,7 @@ private void applyFormula(Network network, String identifiableId, FormulaInfos f private Double applyOperation(Operator operator, Double value1, Double value2) { if (value1 == null || value2 == null || - (value2 == 0 && operator == Operator.DIVISION)) { + value2 == 0 && operator == Operator.DIVISION) { throw new UnsupportedOperationException("TODO"); } diff --git a/src/main/resources/db/changelog/changesets/changelog_20231025T194218Z.xml b/src/main/resources/db/changelog/changesets/changelog_20231026T111001Z.xml similarity index 84% rename from src/main/resources/db/changelog/changesets/changelog_20231025T194218Z.xml rename to src/main/resources/db/changelog/changesets/changelog_20231026T111001Z.xml index c0923bd9d..682a02759 100644 --- a/src/main/resources/db/changelog/changesets/changelog_20231025T194218Z.xml +++ b/src/main/resources/db/changelog/changesets/changelog_20231026T111001Z.xml @@ -1,6 +1,6 @@ - + @@ -8,7 +8,7 @@ - + @@ -18,11 +18,12 @@ - + + @@ -30,7 +31,7 @@ - + @@ -40,25 +41,25 @@ - + - + - + - + - + - + - + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index ade83dd26..6f146a62a 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -202,5 +202,5 @@ databaseChangeLog: file: changesets/changelog_20230801T194601Z.xml relativeToChangelogFile: true - include: - file: changesets/changelog_20231025T194218Z.xml + file: changesets/changelog_20231026T111001Z.xml relativeToChangelogFile: true diff --git a/src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java new file mode 100644 index 000000000..4833a7864 --- /dev/null +++ b/src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java @@ -0,0 +1,241 @@ +/** + * 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; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.matching.StringValuePattern; +import com.powsybl.iidm.network.IdentifiableType; +import com.powsybl.iidm.network.Network; +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.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.BatteryField; +import org.gridsuite.modification.server.dto.formula.equipmentfield.GeneratorField; +import org.gridsuite.modification.server.service.FilterService; +import org.gridsuite.modification.server.utils.NetworkCreation; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Tag; +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.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Seddik Yengui + */ + +@Tag("IntegrationTest") +public abstract class AbstractByFormulaModificationTest extends AbstractNetworkModificationTest { + public static final String PATH = "/v1/filters/export"; + + @Before + public void specificSetUp() { + FilterService.setFilterServerBaseUri(wireMockServer.baseUrl()); + + createEquipments(); + } + + @Test + @Override + public void testCreate() throws Exception { + List filters = getTestFilters(); + UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching(getPath(getNetworkUuid(), true) + "(.+,){4}.*")) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(filters)) + .withHeader("Content-Type", "application/json"))).getId(); + + super.testCreate(); + + wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), filters.stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false); + + } + + @Test + @Override + public void testCopy() throws Exception { + + List filters = getTestFilters(); + UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching(getPath(getNetworkUuid(), true) + "(.+,){4}.*")) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(filters)) + .withHeader("Content-Type", "application/json"))).getId(); + + super.testCopy(); + + wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), filters.stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false); + } + + @Test + public void testCreateWithError() throws Exception { + UUID filterId = UUID.randomUUID(); + FilterEquipments filter = getFilterEquipments(filterId, "filterWithWrongId", List.of(), List.of("wrongId1", "wrongId2")); + var filterInfo = FilterInfos.builder() + .id(filterId) + .name("filterWithWrongId") + .build(); + + UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/filters/export\\?networkUuid=" + getNetworkUuid() + "&variantId=variant_1&ids=" + filterId)) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(List.of(filter))) + .withHeader("Content-Type", "application/json"))).getId(); + + FormulaInfos formulaInfos = FormulaInfos.builder() + .filters(List.of(filterInfo)) + .editedField(GeneratorField.ACTIVE_POWER_SET_POINT.name()) + .fieldOrValue1(ReferenceFieldOrValue.builder().value(55.).build()) + .operator(Operator.ADDITION) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(20.).build()) + .build(); + + ByFormulaModificationInfos byFormulaModificationInfos = ByFormulaModificationInfos.builder() + .formulaInfosList(List.of(formulaInfos)) + .identifiableType(IdentifiableType.GENERATOR) + .build(); + + String modificationToCreateJson = mapper.writeValueAsString(byFormulaModificationInfos); + + MvcResult mvcResult = mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn(); + + Optional networkModificationResult = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { }); + assertTrue(networkModificationResult.isPresent()); + assertEquals(NetworkModificationResult.ApplicationStatus.WITH_ERRORS, networkModificationResult.get().getApplicationStatus()); + + wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), List.of(filterId)), false); + } + + @Test + public void testCreateWithWarning() throws Exception { + UUID filterId = UUID.randomUUID(); + String equipmentId = "v3Battery"; + IdentifiableAttributes identifiableAttributes1 = getIdentifiableAttributes(equipmentId, 1.0); + FilterEquipments filter = getFilterEquipments(filterId, "filterWithWrongId", List.of(identifiableAttributes1), List.of("wrongId")); + var filterInfo = FilterInfos.builder() + .id(filterId) + .name("filterWithWrongId") + .build(); + + UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/filters/export\\?networkUuid=" + getNetworkUuid() + "&variantId=variant_1&ids=" + filterId)) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(List.of(filter))) + .withHeader("Content-Type", "application/json"))).getId(); + + FormulaInfos formulaInfos = FormulaInfos.builder() + .filters(List.of(filterInfo)) + .editedField(BatteryField.ACTIVE_POWER_SET_POINT.name()) + .fieldOrValue1(ReferenceFieldOrValue.builder().value(55.).build()) + .operator(Operator.ADDITION) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(20.).build()) + .build(); + + ByFormulaModificationInfos byFormulaModificationInfos = ByFormulaModificationInfos.builder() + .formulaInfosList(List.of(formulaInfos)) + .identifiableType(IdentifiableType.BATTERY) + .build(); + + String modificationToCreateJson = mapper.writeValueAsString(byFormulaModificationInfos); + + MvcResult mvcResult = mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn(); + + Optional networkModificationResult = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { }); + assertTrue(networkModificationResult.isPresent()); + assertEquals(NetworkModificationResult.ApplicationStatus.WITH_WARNINGS, networkModificationResult.get().getApplicationStatus()); + + wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), List.of(filterId)), false); + } + + @Override + protected Network createNetwork(UUID networkUuid) { + return NetworkCreation.create(networkUuid, true); + } + + @Override + protected ByFormulaModificationInfos buildModification() { + return ByFormulaModificationInfos.builder() + .identifiableType(IdentifiableType.GENERATOR) + .formulaInfosList(getFormulaInfos()) + .build(); + } + + @Override + protected ByFormulaModificationInfos buildModificationUpdate() { + return ByFormulaModificationInfos.builder() + .identifiableType(IdentifiableType.GENERATOR) + .formulaInfosList(getUpdatedFormulaInfos()) + .build(); + } + + IdentifiableAttributes getIdentifiableAttributes(String id, Double distributionKey) { + return IdentifiableAttributes.builder() + .id(id) + .type(IdentifiableType.GENERATOR) + .distributionKey(distributionKey) + .build(); + } + + FilterEquipments getFilterEquipments(UUID filterID, String filterName, List identifiableAttributes, List notFoundEquipments) { + return FilterEquipments.builder() + .filterId(filterID) + .filterName(filterName) + .identifiableAttributes(identifiableAttributes) + .notFoundEquipments(notFoundEquipments) + .build(); + } + + FormulaInfos getFormulaInfo(String editedField, + List filters, + Operator operator, + ReferenceFieldOrValue fieldOrValue1, + ReferenceFieldOrValue fieldOrValue2) { + return FormulaInfos.builder() + .editedField(editedField) + .filters(filters) + .operator(operator) + .fieldOrValue1(fieldOrValue1) + .fieldOrValue2(fieldOrValue2) + .build(); + } + + private 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) { + if (isRegexPhat) { + return "/v1/filters/export\\?networkUuid=" + networkUuid + "\\&variantId=variant_1\\&ids="; + } + return "/v1/filters/export?networkUuid=" + networkUuid + "&variantId=variant_1&ids="; + } + + abstract void createEquipments(); + + abstract List getTestFilters(); + + abstract List getFormulaInfos(); + + abstract List getUpdatedFormulaInfos(); +} diff --git a/src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java new file mode 100644 index 000000000..add41fa6e --- /dev/null +++ b/src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java @@ -0,0 +1,242 @@ +/** + * 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; + +import com.powsybl.iidm.network.extensions.ActivePowerControl; +import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; +import org.gridsuite.modification.server.dto.FilterEquipments; +import org.gridsuite.modification.server.dto.FilterInfos; +import org.gridsuite.modification.server.dto.IdentifiableAttributes; +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.BatteryField; +import org.gridsuite.modification.server.dto.formula.equipmentfield.GeneratorField; + +import java.util.List; +import java.util.UUID; + +import static org.gridsuite.modification.server.utils.NetworkUtil.createBattery; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Seddik Yengui + */ + +public class BatteryByFormulaModificationTest extends AbstractByFormulaModificationTest { + private static final UUID FILTER_ID_1 = UUID.randomUUID(); + private static final UUID FILTER_ID_2 = UUID.randomUUID(); + private static final UUID FILTER_ID_3 = UUID.randomUUID(); + private static final UUID FILTER_ID_4 = UUID.randomUUID(); + private static final UUID FILTER_ID_5 = UUID.randomUUID(); + private static final String BATTERY_ID_1 = "v3Battery"; + private static final String BATTERY_ID_2 = "battery2"; + private static final String BATTERY_ID_3 = "battery3"; + private static final String BATTERY_ID_4 = "battery4"; + private static final String BATTERY_ID_5 = "battery5"; + private static final String BATTERY_ID_6 = "battery6"; + + @Override + void createEquipments() { + getNetwork().getVariantManager().setWorkingVariant("variant_1"); + getNetwork().getBattery(BATTERY_ID_1).setTargetP(100).setMaxP(500).setMinP(0).setTargetQ(80); + getNetwork().getBattery(BATTERY_ID_1).newExtension(ActivePowerControlAdder.class).withDroop(1).add(); + + createBattery(getNetwork().getVoltageLevel("v2"), BATTERY_ID_2, "v2Battery2", 20, 50, 2000, 200, 50); + createBattery(getNetwork().getVoltageLevel("v3"), BATTERY_ID_3, "v3Battery3", 30, 70, 400, 300, 50); + + createBattery(getNetwork().getVoltageLevel("v4"), BATTERY_ID_4, "v4Battery4", 40, 25, 350, 70, 50); + + createBattery(getNetwork().getVoltageLevel("v5"), BATTERY_ID_5, "v5Battery5", 50, 50, 600, 55, 140); + getNetwork().getBattery(BATTERY_ID_5).newExtension(ActivePowerControlAdder.class).withDroop(4).add(); + + createBattery(getNetwork().getVoltageLevel("v6"), BATTERY_ID_6, "v6Battery6", 60, 200, 700, 250, 210); + } + + @Override + List getTestFilters() { + IdentifiableAttributes battery1 = getIdentifiableAttributes(BATTERY_ID_1, 1.0); + IdentifiableAttributes battery2 = getIdentifiableAttributes(BATTERY_ID_2, 2.0); + IdentifiableAttributes battery3 = getIdentifiableAttributes(BATTERY_ID_3, 2.0); + IdentifiableAttributes battery4 = getIdentifiableAttributes(BATTERY_ID_4, 5.0); + IdentifiableAttributes battery5 = getIdentifiableAttributes(BATTERY_ID_5, 6.0); + IdentifiableAttributes battery6 = getIdentifiableAttributes(BATTERY_ID_6, 7.0); + + FilterEquipments filter1 = getFilterEquipments(FILTER_ID_1, "filter1", List.of(battery1, battery2), List.of()); + FilterEquipments filter2 = getFilterEquipments(FILTER_ID_2, "filter2", List.of(battery3, battery4), List.of()); + FilterEquipments filter3 = getFilterEquipments(FILTER_ID_3, "filter3", List.of(battery5, battery6), List.of()); + FilterEquipments filter4 = getFilterEquipments(FILTER_ID_4, "filter4", List.of(battery1, battery5), List.of()); + FilterEquipments filter5 = getFilterEquipments(FILTER_ID_5, "filter5", List.of(battery2, battery3), List.of()); + + return List.of(filter1, filter2, filter3, filter4, filter5); + } + + @Override + List getFormulaInfos() { + var filter1 = FilterInfos.builder() + .id(FILTER_ID_1) + .name("filter1") + .build(); + + var filter2 = FilterInfos.builder() + .id(FILTER_ID_2) + .name("filter2") + .build(); + + var filter3 = FilterInfos.builder() + .id(FILTER_ID_3) + .name("filter3") + .build(); + + var filter4 = FilterInfos.builder() + .id(FILTER_ID_4) + .name("filter4") + .build(); + + var filter5 = FilterInfos.builder() + .id(FILTER_ID_5) + .name("filter5") + .build(); + + ReferenceFieldOrValue maxActivePowerRef = ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MAXIMUM_ACTIVE_POWER.name()).build(); + ReferenceFieldOrValue minActivePowerRef = ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MINIMUM_ACTIVE_POWER.name()).build(); + + FormulaInfos formulaInfos1 = getFormulaInfo(BatteryField.MAXIMUM_ACTIVE_POWER.name(), + List.of(filter1, filter2), + Operator.ADDITION, + maxActivePowerRef, + ReferenceFieldOrValue.builder().value(50.).build()); + + FormulaInfos formulaInfos2 = getFormulaInfo(BatteryField.MINIMUM_ACTIVE_POWER.name(), + List.of(filter3), + Operator.MODULUS, + minActivePowerRef, + ReferenceFieldOrValue.builder().value(30.).build()); + + FormulaInfos formulaInfos3 = getFormulaInfo(BatteryField.ACTIVE_POWER_SET_POINT.name(), + List.of(filter5), + Operator.SUBTRACTION, + maxActivePowerRef, + minActivePowerRef); + + FormulaInfos formulaInfos4 = getFormulaInfo(BatteryField.REACTIVE_POWER_SET_POINT.name(), + List.of(filter4), + Operator.DIVISION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.REACTIVE_POWER_SET_POINT.name()).build(), + ReferenceFieldOrValue.builder().value(2.).build()); + + FormulaInfos formulaInfos5 = getFormulaInfo(BatteryField.DROOP.name(), + List.of(filter4), + Operator.MULTIPLICATION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.DROOP.name()).build(), + ReferenceFieldOrValue.builder().value(2.).build()); + + return List.of(formulaInfos1, formulaInfos2, formulaInfos3, formulaInfos4, formulaInfos5); + } + + @Override + List getUpdatedFormulaInfos() { + var filter1 = FilterInfos.builder() + .id(FILTER_ID_1) + .name("filter1") + .build(); + + var filter2 = FilterInfos.builder() + .id(FILTER_ID_2) + .name("filter2") + .build(); + + var filter3 = FilterInfos.builder() + .id(FILTER_ID_3) + .name("filter3") + .build(); + + var filter4 = FilterInfos.builder() + .id(FILTER_ID_4) + .name("filter4") + .build(); + + var filter5 = FilterInfos.builder() + .id(FILTER_ID_5) + .name("filter5") + .build(); + + FormulaInfos formulaInfos1 = FormulaInfos.builder() + .editedField(BatteryField.MAXIMUM_ACTIVE_POWER.name()) + .fieldOrValue1(ReferenceFieldOrValue.builder().value(200.).build()) + .fieldOrValue2(ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MAXIMUM_ACTIVE_POWER.name()).build()) + .operator(Operator.ADDITION) + .filters(List.of(filter1, filter2)) + .build(); + + FormulaInfos formulaInfos2 = FormulaInfos.builder() + .editedField(BatteryField.MINIMUM_ACTIVE_POWER.name()) + .fieldOrValue1(ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MINIMUM_ACTIVE_POWER.name()).build()) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(35.).build()) + .operator(Operator.MODULUS) + .filters(List.of(filter3)) + .build(); + + FormulaInfos formulaInfos3 = FormulaInfos.builder() + .editedField(BatteryField.ACTIVE_POWER_SET_POINT.name()) + .fieldOrValue1(ReferenceFieldOrValue.builder().equipmentField(GeneratorField.ACTIVE_POWER_SET_POINT.name()).build()) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(10.).build()) + .operator(Operator.ADDITION) + .filters(List.of(filter5)) + .build(); + + return List.of(formulaInfos1, formulaInfos2, formulaInfos3); + } + + @Override + protected void assertAfterNetworkModificationCreation() { + assertEquals(550, getNetwork().getBattery(BATTERY_ID_1).getMaxP(), 0); + assertEquals(40, getNetwork().getBattery(BATTERY_ID_1).getTargetQ(), 0); + ActivePowerControl activePowerControl1 = getNetwork().getBattery(BATTERY_ID_1).getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl1); + assertEquals(2, activePowerControl1.getDroop(), 0); + + assertEquals(2050, getNetwork().getBattery(BATTERY_ID_2).getMaxP(), 0); + assertEquals(2000, getNetwork().getBattery(BATTERY_ID_2).getTargetP(), 0); + assertEquals(450, getNetwork().getBattery(BATTERY_ID_3).getMaxP(), 0); + assertEquals(380, getNetwork().getBattery(BATTERY_ID_3).getTargetP(), 0); + assertEquals(400, getNetwork().getBattery(BATTERY_ID_4).getMaxP(), 0); + + assertEquals(20, getNetwork().getBattery(BATTERY_ID_5).getMinP(), 0); + assertEquals(70, getNetwork().getBattery(BATTERY_ID_5).getTargetQ(), 0); + ActivePowerControl activePowerControl5 = getNetwork().getBattery(BATTERY_ID_5).getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl5); + assertEquals(8, activePowerControl5.getDroop(), 0); + + assertEquals(20, getNetwork().getBattery(BATTERY_ID_6).getMinP(), 0); + } + + @Override + protected void assertAfterNetworkModificationDeletion() { + assertEquals(500, getNetwork().getBattery(BATTERY_ID_1).getMaxP(), 0); + assertEquals(80, getNetwork().getBattery(BATTERY_ID_1).getTargetQ(), 0); + ActivePowerControl activePowerControl1 = getNetwork().getBattery(BATTERY_ID_1).getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl1); + assertEquals(1, activePowerControl1.getDroop(), 0); + + assertEquals(2000, getNetwork().getBattery(BATTERY_ID_2).getMaxP(), 0); + assertEquals(200, getNetwork().getBattery(BATTERY_ID_2).getTargetP(), 0); + assertEquals(400, getNetwork().getBattery(BATTERY_ID_3).getMaxP(), 0); + assertEquals(300, getNetwork().getBattery(BATTERY_ID_3).getTargetP(), 0); + assertEquals(350, getNetwork().getBattery(BATTERY_ID_4).getMaxP(), 0); + + assertEquals(50, getNetwork().getBattery(BATTERY_ID_5).getMinP(), 0); + assertEquals(140, getNetwork().getBattery(BATTERY_ID_5).getTargetQ(), 0); + ActivePowerControl activePowerControl5 = getNetwork().getBattery(BATTERY_ID_5).getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl5); + assertEquals(4, activePowerControl5.getDroop(), 0); + + assertEquals(200, getNetwork().getBattery(BATTERY_ID_6).getMinP(), 0); + } +} diff --git a/src/test/java/org/gridsuite/modification/server/modifications/GeneratorByFormulaModificationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/GeneratorByFormulaModificationTest.java new file mode 100644 index 000000000..d8a438bc9 --- /dev/null +++ b/src/test/java/org/gridsuite/modification/server/modifications/GeneratorByFormulaModificationTest.java @@ -0,0 +1,447 @@ +/** + * 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; + +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.extensions.ActivePowerControl; +import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import com.powsybl.iidm.network.extensions.CoordinatedReactiveControl; +import com.powsybl.iidm.network.extensions.CoordinatedReactiveControlAdder; +import com.powsybl.iidm.network.extensions.GeneratorShortCircuit; +import com.powsybl.iidm.network.extensions.GeneratorShortCircuitAdder; +import com.powsybl.iidm.network.extensions.GeneratorStartup; +import com.powsybl.iidm.network.extensions.GeneratorStartupAdder; +import org.gridsuite.modification.server.dto.FilterEquipments; +import org.gridsuite.modification.server.dto.FilterInfos; +import org.gridsuite.modification.server.dto.IdentifiableAttributes; +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.GeneratorField; +import org.junit.jupiter.api.Tag; + +import java.util.List; +import java.util.UUID; + +import static org.gridsuite.modification.server.utils.NetworkUtil.createGenerator; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Seddik Yengui + */ + +@Tag("IntegrationTest") +public class GeneratorByFormulaModificationTest extends AbstractByFormulaModificationTest { + private static final UUID FILTER_ID_1 = UUID.randomUUID(); + private static final UUID FILTER_ID_2 = UUID.randomUUID(); + private static final UUID FILTER_ID_3 = UUID.randomUUID(); + private static final UUID FILTER_ID_4 = UUID.randomUUID(); + private static final UUID FILTER_ID_5 = UUID.randomUUID(); + private static final String GENERATOR_ID_1 = "idGenerator"; + private static final String GENERATOR_ID_2 = "v5generator"; + private static final String GENERATOR_ID_3 = "v6generator"; + private static final String GENERATOR_ID_4 = "gen4"; + private static final String GENERATOR_ID_5 = "gen5"; + private static final String GENERATOR_ID_6 = "gen6"; + private static final String GENERATOR_ID_7 = "gen7"; + private static final String GENERATOR_ID_8 = "gen8"; + private static final String GENERATOR_ID_9 = "gen9"; + private static final String GENERATOR_ID_10 = "gen10"; + + void createEquipments() { + getNetwork().getVariantManager().setWorkingVariant("variant_1"); + getNetwork().getGenerator(GENERATOR_ID_1) + .setTargetP(100) + .setMaxP(500) + .setMinP(0) + .newExtension(GeneratorStartupAdder.class) + .withMarginalCost(30.) + .withPlannedOutageRate(25.) + .withPlannedActivePowerSetpoint(40.) + .withForcedOutageRate(55.) + .add(); + + getNetwork().getGenerator(GENERATOR_ID_2) + .setTargetP(200) + .setMaxP(2000) + .setMinP(50) + .newExtension(GeneratorStartupAdder.class) + .withMarginalCost(30.) + .withPlannedOutageRate(25.) + .withPlannedActivePowerSetpoint(40.) + .withForcedOutageRate(55.) + .add(); + + getNetwork().getGenerator(GENERATOR_ID_3) + .setTargetP(300) + .setMaxP(2000) + .setMinP(70) + .newExtension(GeneratorShortCircuitAdder.class) + .withDirectTransX(40.) + .withStepUpTransformerX(38.) + .add(); + + createGenerator(getNetwork().getVoltageLevel("v1"), GENERATOR_ID_4, 3, 400, 1.0, "cn10", 11, ConnectablePosition.Direction.TOP, 700, 110); + getNetwork().getGenerator(GENERATOR_ID_4) + .newExtension(GeneratorShortCircuitAdder.class) + .withDirectTransX(46.) + .withStepUpTransformerX(50.) + .add(); + + createGenerator(getNetwork().getVoltageLevel("v1"), GENERATOR_ID_5, 20, 200, 1.0, "cn10", 12, ConnectablePosition.Direction.TOP, 2000, 50); + getNetwork().getGenerator(GENERATOR_ID_5).newExtension(ActivePowerControlAdder.class).withDroop(2).add(); + + createGenerator(getNetwork().getVoltageLevel("v2"), GENERATOR_ID_6, 11, 100, 1.0, "cn10", 13, ConnectablePosition.Direction.TOP, 500, 20); + getNetwork().getGenerator(GENERATOR_ID_6).newExtension(ActivePowerControlAdder.class).withDroop(3).add(); + + createGenerator(getNetwork().getVoltageLevel("v6"), GENERATOR_ID_7, 10, 200, 1.0, "cn10", 14, ConnectablePosition.Direction.TOP, 2000, 50); + getNetwork().getGenerator(GENERATOR_ID_7).newExtension(CoordinatedReactiveControlAdder.class) + .withQPercent(6) + .add(); + getNetwork().getGenerator(GENERATOR_ID_7).newExtension(GeneratorStartupAdder.class).withMarginalCost(50).add(); + + createGenerator(getNetwork().getVoltageLevel("v3"), GENERATOR_ID_8, 10, 100, 1.0, "cn10", 15, ConnectablePosition.Direction.TOP, 500, 20); + getNetwork().getGenerator(GENERATOR_ID_8).newExtension(CoordinatedReactiveControlAdder.class) + .withQPercent(12) + .add(); + getNetwork().getGenerator(GENERATOR_ID_8).newExtension(GeneratorStartupAdder.class).withMarginalCost(60).add(); + + createGenerator(getNetwork().getVoltageLevel("v4"), GENERATOR_ID_9, 10, 200, 1.0, "cn10", 16, ConnectablePosition.Direction.TOP, 2000, 50); + getNetwork().getGenerator(GENERATOR_ID_9).setRatedS(60.); + + createGenerator(getNetwork().getVoltageLevel("v5"), GENERATOR_ID_10, 10, 100, 1.0, "cn10", 17, ConnectablePosition.Direction.TOP, 500, 20); + getNetwork().getGenerator(GENERATOR_ID_10).setRatedS(30.); + } + + List getTestFilters() { + IdentifiableAttributes gen1 = getIdentifiableAttributes(GENERATOR_ID_1, 1.0); + IdentifiableAttributes gen2 = getIdentifiableAttributes(GENERATOR_ID_2, 2.0); + IdentifiableAttributes gen3 = getIdentifiableAttributes(GENERATOR_ID_3, 2.0); + IdentifiableAttributes gen4 = getIdentifiableAttributes(GENERATOR_ID_4, 5.0); + IdentifiableAttributes gen5 = getIdentifiableAttributes(GENERATOR_ID_5, 6.0); + IdentifiableAttributes gen6 = getIdentifiableAttributes(GENERATOR_ID_6, 7.0); + IdentifiableAttributes gen7 = getIdentifiableAttributes(GENERATOR_ID_7, 3.0); + IdentifiableAttributes gen8 = getIdentifiableAttributes(GENERATOR_ID_8, 8.0); + IdentifiableAttributes gen9 = getIdentifiableAttributes(GENERATOR_ID_9, 0.0); + IdentifiableAttributes gen10 = getIdentifiableAttributes(GENERATOR_ID_10, 9.0); + + FilterEquipments filter1 = getFilterEquipments(FILTER_ID_1, "filter1", List.of(gen1, gen2), List.of()); + FilterEquipments filter2 = getFilterEquipments(FILTER_ID_2, "filter2", List.of(gen3, gen4), List.of()); + FilterEquipments filter3 = getFilterEquipments(FILTER_ID_3, "filter3", List.of(gen5, gen6), List.of()); + FilterEquipments filter4 = getFilterEquipments(FILTER_ID_4, "filter4", List.of(gen7, gen8), List.of()); + FilterEquipments filter5 = getFilterEquipments(FILTER_ID_5, "filter5", List.of(gen9, gen10), List.of()); + + return List.of(filter1, filter2, filter3, filter4, filter5); + } + + @Override + List getFormulaInfos() { + var filter1 = FilterInfos.builder() + .id(FILTER_ID_1) + .name("filter1") + .build(); + + var filter2 = FilterInfos.builder() + .id(FILTER_ID_2) + .name("filter2") + .build(); + + var filter3 = FilterInfos.builder() + .id(FILTER_ID_3) + .name("filter3") + .build(); + + var filter4 = FilterInfos.builder() + .id(FILTER_ID_4) + .name("filter4") + .build(); + + var filter5 = FilterInfos.builder() + .id(FILTER_ID_5) + .name("filter5") + .build(); + + FormulaInfos formulaInfos1 = getFormulaInfo(GeneratorField.ACTIVE_POWER_SET_POINT.name(), + List.of(filter1, filter2), + Operator.ADDITION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MINIMUM_ACTIVE_POWER.name()).build(), + ReferenceFieldOrValue.builder().value(50.).build()); + + FormulaInfos formulaInfos2 = getFormulaInfo(GeneratorField.DROOP.name(), + List.of(filter3), + Operator.MULTIPLICATION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.DROOP.name()).build(), + ReferenceFieldOrValue.builder().value(2.).build()); + + FormulaInfos formulaInfos3 = getFormulaInfo(GeneratorField.RATED_NOMINAL_POWER.name(), + List.of(filter5), + Operator.DIVISION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MAXIMUM_ACTIVE_POWER.name()).build(), + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MINIMUM_ACTIVE_POWER.name()).build()); + + FormulaInfos formulaInfos4 = getFormulaInfo(GeneratorField.MARGINAL_COST.name(), + List.of(filter1), + Operator.DIVISION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MARGINAL_COST.name()).build(), + ReferenceFieldOrValue.builder().value(2.).build()); + + FormulaInfos formulaInfos5 = getFormulaInfo(GeneratorField.VOLTAGE_SET_POINT.name(), + List.of(filter4), + Operator.MULTIPLICATION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.VOLTAGE_SET_POINT.name()).build(), + ReferenceFieldOrValue.builder().value(2.).build()); + + FormulaInfos formulaInfos6 = getFormulaInfo(GeneratorField.PLANNED_ACTIVE_POWER_SET_POINT.name(), + List.of(filter1), + Operator.ADDITION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.PLANNED_ACTIVE_POWER_SET_POINT.name()).build(), + ReferenceFieldOrValue.builder().value(10.).build()); + + FormulaInfos formulaInfos7 = getFormulaInfo(GeneratorField.MINIMUM_ACTIVE_POWER.name(), + List.of(filter1, filter2), + Operator.ADDITION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MINIMUM_ACTIVE_POWER.name()).build(), + ReferenceFieldOrValue.builder().value(50.).build()); + + FormulaInfos formulaInfos8 = getFormulaInfo(GeneratorField.PLANNED_OUTAGE_RATE.name(), + List.of(filter1), + Operator.MULTIPLICATION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.PLANNED_OUTAGE_RATE.name()).build(), + ReferenceFieldOrValue.builder().value(0.1).build()); + + FormulaInfos formulaInfos9 = getFormulaInfo(GeneratorField.FORCED_OUTAGE_RATE.name(), + List.of(filter1), + Operator.DIVISION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.FORCED_OUTAGE_RATE.name()).build(), + ReferenceFieldOrValue.builder().value(0.05).build()); + + FormulaInfos formulaInfos10 = getFormulaInfo(GeneratorField.MAXIMUM_ACTIVE_POWER.name(), + List.of(filter1, filter2, filter3, filter4, filter5), + Operator.ADDITION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MAXIMUM_ACTIVE_POWER.name()).build(), + ReferenceFieldOrValue.builder().value(2.).build()); + + FormulaInfos formulaInfos11 = getFormulaInfo(GeneratorField.TRANSIENT_REACTANCE.name(), + List.of(filter2), + Operator.SUBTRACTION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.TRANSIENT_REACTANCE.name()).build(), + ReferenceFieldOrValue.builder().value(0.2).build()); + + FormulaInfos formulaInfos12 = getFormulaInfo(GeneratorField.STEP_UP_TRANSFORMER_REACTANCE.name(), + List.of(filter2), + Operator.MULTIPLICATION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.STEP_UP_TRANSFORMER_REACTANCE.name()).build(), + ReferenceFieldOrValue.builder().value(0.3).build()); + + FormulaInfos formulaInfos13 = getFormulaInfo(GeneratorField.Q_PERCENT.name(), + List.of(filter4), + Operator.DIVISION, + ReferenceFieldOrValue.builder().equipmentField(GeneratorField.Q_PERCENT.name()).build(), + ReferenceFieldOrValue.builder().value(0.25).build()); + + return List.of(formulaInfos1, + formulaInfos2, + formulaInfos3, + formulaInfos4, + formulaInfos5, + formulaInfos6, + formulaInfos7, + formulaInfos8, + formulaInfos9, + formulaInfos10, + formulaInfos11, + formulaInfos12, + formulaInfos13); + } + + @Override + List getUpdatedFormulaInfos() { + var filter1 = FilterInfos.builder() + .id(FILTER_ID_1) + .name("filter1") + .build(); + + var filter2 = FilterInfos.builder() + .id(FILTER_ID_2) + .name("filter2") + .build(); + + var filter3 = FilterInfos.builder() + .id(FILTER_ID_3) + .name("filter3") + .build(); + + FormulaInfos formulaInfos1 = FormulaInfos.builder() + .editedField(GeneratorField.REACTIVE_POWER_SET_POINT.name()) + .fieldOrValue1(ReferenceFieldOrValue.builder().value(2.).build()) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(50.).build()) + .operator(Operator.MULTIPLICATION) + .filters(List.of(filter1, filter2)) + .build(); + + FormulaInfos formulaInfos2 = FormulaInfos.builder() + .editedField(GeneratorField.MINIMUM_ACTIVE_POWER.name()) + .fieldOrValue1(ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MINIMUM_ACTIVE_POWER.name()).build()) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(0.5).build()) + .operator(Operator.DIVISION) + .filters(List.of(filter3)) + .build(); + + return List.of(formulaInfos1, formulaInfos2); + } + + @Override + protected void assertAfterNetworkModificationCreation() { + Generator generator1 = getNetwork().getGenerator(GENERATOR_ID_1); + GeneratorStartup generatorStartup1 = generator1.getExtension(GeneratorStartup.class); + assertNotNull(generatorStartup1); + assertEquals(50, generator1.getTargetP(), 0); + assertEquals(15, generatorStartup1.getMarginalCost(), 0); + assertEquals(55, generatorStartup1.getPlannedOutageRate(), 0); + assertEquals(1100, generatorStartup1.getForcedOutageRate(), 0); + assertEquals(50, generatorStartup1.getPlannedActivePowerSetpoint(), 0); + assertEquals(502, generator1.getMaxP(), 0); + assertEquals(50, generator1.getMinP(), 0); + + Generator generator2 = getNetwork().getGenerator(GENERATOR_ID_2); + GeneratorStartup generatorStartup2 = generator2.getExtension(GeneratorStartup.class); + assertNotNull(generatorStartup2); + assertEquals(100, generator2.getTargetP(), 0); + assertEquals(15, generatorStartup2.getMarginalCost(), 0); + assertEquals(55, generatorStartup2.getPlannedOutageRate(), 0); + assertEquals(1100, generatorStartup2.getForcedOutageRate(), 0); + assertEquals(50, generatorStartup2.getPlannedActivePowerSetpoint(), 0); + assertEquals(2002, generator2.getMaxP(), 0); + assertEquals(100, generator2.getMinP(), 0); + + Generator generator3 = getNetwork().getGenerator(GENERATOR_ID_3); + GeneratorShortCircuit generatorShortCircuit3 = generator3.getExtension(GeneratorShortCircuit.class); + assertNotNull(generatorShortCircuit3); + assertEquals(120, generator3.getTargetP(), 0); + assertEquals(39.8, generatorShortCircuit3.getDirectTransX(), 0); + assertEquals(11.4, generatorShortCircuit3.getStepUpTransformerX(), 0); + assertEquals(2002, generator3.getMaxP(), 0); + assertEquals(120, generator3.getMinP(), 0); + + Generator generator4 = getNetwork().getGenerator(GENERATOR_ID_4); + GeneratorShortCircuit generatorShortCircuit4 = generator4.getExtension(GeneratorShortCircuit.class); + assertNotNull(generatorShortCircuit4); + assertEquals(45.8, generatorShortCircuit4.getDirectTransX(), 0); + assertEquals(15.0, generatorShortCircuit4.getStepUpTransformerX(), 0); + assertEquals(160, generator4.getTargetP(), 0); + assertEquals(702, generator4.getMaxP(), 0); + assertEquals(160, generator4.getMinP(), 0); + + Generator generator5 = getNetwork().getGenerator(GENERATOR_ID_5); + ActivePowerControl activePowerControl5 = generator5.getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl5); + assertEquals(2002, generator5.getMaxP(), 0); + assertEquals(4, activePowerControl5.getDroop(), 0); + + Generator generator6 = getNetwork().getGenerator(GENERATOR_ID_6); + ActivePowerControl activePowerControl6 = generator6.getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl6); + assertEquals(502, generator6.getMaxP(), 0); + assertEquals(6, activePowerControl6.getDroop(), 0); + + Generator generator7 = getNetwork().getGenerator(GENERATOR_ID_7); + CoordinatedReactiveControl coordinatedReactiveControl7 = generator7.getExtension(CoordinatedReactiveControl.class); + assertNotNull(coordinatedReactiveControl7); + GeneratorStartup generatorStartup7 = generator7.getExtension(GeneratorStartup.class); + assertNotNull(generatorStartup7); + assertEquals(50, generatorStartup7.getMarginalCost(), 0); + assertEquals(24, coordinatedReactiveControl7.getQPercent(), 0); + + Generator generator8 = getNetwork().getGenerator(GENERATOR_ID_8); + CoordinatedReactiveControl coordinatedReactiveControl8 = generator8.getExtension(CoordinatedReactiveControl.class); + assertNotNull(coordinatedReactiveControl8); + GeneratorStartup generatorStartup8 = generator8.getExtension(GeneratorStartup.class); + assertNotNull(generatorStartup8); + assertEquals(60, generatorStartup8.getMarginalCost(), 0); + assertEquals(48, coordinatedReactiveControl8.getQPercent(), 0); + + assertEquals(40, getNetwork().getGenerator(GENERATOR_ID_9).getRatedS(), 0); + assertEquals(25, getNetwork().getGenerator(GENERATOR_ID_10).getRatedS(), 0); + } + + @Override + protected void assertAfterNetworkModificationDeletion() { + Generator generator1 = getNetwork().getGenerator(GENERATOR_ID_1); + GeneratorStartup generatorStartup1 = generator1.getExtension(GeneratorStartup.class); + assertNotNull(generatorStartup1); + assertEquals(100, generator1.getTargetP(), 0); + assertEquals(30, generatorStartup1.getMarginalCost(), 0); + assertEquals(25, generatorStartup1.getPlannedOutageRate(), 0); + assertEquals(55, generatorStartup1.getForcedOutageRate(), 0); + assertEquals(40, generatorStartup1.getPlannedActivePowerSetpoint(), 0); + assertEquals(500, generator1.getMaxP(), 0); + assertEquals(0, generator1.getMinP(), 0); + + Generator generator2 = getNetwork().getGenerator(GENERATOR_ID_2); + GeneratorStartup generatorStartup2 = generator2.getExtension(GeneratorStartup.class); + assertNotNull(generatorStartup2); + assertEquals(200, generator2.getTargetP(), 0); + assertEquals(30, generatorStartup2.getMarginalCost(), 0); + assertEquals(25, generatorStartup2.getPlannedOutageRate(), 0); + assertEquals(55, generatorStartup2.getForcedOutageRate(), 0); + assertEquals(40, generatorStartup2.getPlannedActivePowerSetpoint(), 0); + assertEquals(2000, generator2.getMaxP(), 0); + assertEquals(50, generator2.getMinP(), 0); + + Generator generator3 = getNetwork().getGenerator(GENERATOR_ID_3); + GeneratorShortCircuit generatorShortCircuit3 = generator3.getExtension(GeneratorShortCircuit.class); + assertNotNull(generatorShortCircuit3); + assertEquals(300, generator3.getTargetP(), 0); + assertEquals(40, generatorShortCircuit3.getDirectTransX(), 0); + assertEquals(38, generatorShortCircuit3.getStepUpTransformerX(), 0); + assertEquals(2000, generator3.getMaxP(), 0); + assertEquals(70, generator3.getMinP(), 0); + + Generator generator4 = getNetwork().getGenerator(GENERATOR_ID_4); + GeneratorShortCircuit generatorShortCircuit4 = generator4.getExtension(GeneratorShortCircuit.class); + assertNotNull(generatorShortCircuit4); + assertEquals(46, generatorShortCircuit4.getDirectTransX(), 0); + assertEquals(50, generatorShortCircuit4.getStepUpTransformerX(), 0); + assertEquals(400, generator4.getTargetP(), 0); + assertEquals(700, generator4.getMaxP(), 0); + assertEquals(110, generator4.getMinP(), 0); + + Generator generator5 = getNetwork().getGenerator(GENERATOR_ID_5); + ActivePowerControl activePowerControl5 = generator5.getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl5); + assertEquals(2000, generator5.getMaxP(), 0); + assertEquals(2, activePowerControl5.getDroop(), 0); + + Generator generator6 = getNetwork().getGenerator(GENERATOR_ID_6); + ActivePowerControl activePowerControl6 = generator6.getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl6); + assertEquals(500, generator6.getMaxP(), 0); + assertEquals(3, activePowerControl6.getDroop(), 0); + + Generator generator7 = getNetwork().getGenerator(GENERATOR_ID_7); + CoordinatedReactiveControl coordinatedReactiveControl7 = generator7.getExtension(CoordinatedReactiveControl.class); + assertNotNull(coordinatedReactiveControl7); + GeneratorStartup generatorStartup7 = generator7.getExtension(GeneratorStartup.class); + assertNotNull(generatorStartup7); + assertEquals(50, generatorStartup7.getMarginalCost(), 0); + assertEquals(6, coordinatedReactiveControl7.getQPercent(), 0); + + Generator generator8 = getNetwork().getGenerator(GENERATOR_ID_8); + CoordinatedReactiveControl coordinatedReactiveControl8 = generator8.getExtension(CoordinatedReactiveControl.class); + assertNotNull(coordinatedReactiveControl8); + GeneratorStartup generatorStartup8 = generator8.getExtension(GeneratorStartup.class); + assertNotNull(generatorStartup8); + assertEquals(60, generatorStartup8.getMarginalCost(), 0); + assertEquals(12, coordinatedReactiveControl8.getQPercent(), 0); + + assertEquals(60, getNetwork().getGenerator(GENERATOR_ID_9).getRatedS(), 0); + assertEquals(30, getNetwork().getGenerator(GENERATOR_ID_10).getRatedS(), 0); + } +} From 3adfc99b73226ec9e2c00f475ceab496af6b459e Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Mon, 30 Oct 2023 13:41:44 +0100 Subject: [PATCH 04/20] fixes --- .../modifications/ByFormulaModification.java | 14 ++- .../db/changelog/db.changelog-master.yaml | 2 +- .../AbstractByFormulaModificationTest.java | 80 +---------------- .../BatteryByFormulaModificationTest.java | 87 +++++++++++++++++-- .../GeneratorByFormulaModificationTest.java | 72 +++++++++++++++ 5 files changed, 168 insertions(+), 87 deletions(-) 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 284e0b38f..13e4ff01d 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -82,11 +82,14 @@ public void apply(Network network, Reporter subReporter) { modificationInfos.getFormulaInfosList().forEach(formulaInfos -> formulaInfos.getFilters().forEach(filterInfos -> { var filterEquipments = exportFilters.get(filterInfos.getId()); - filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, attributes.getId(), formulaInfos)); + filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, attributes.getId(), formulaInfos, subReporter)); })); + + createReport(subReporter, "byFormulaModification", "new modification by formula", TypedValue.INFO_SEVERITY); + } - private void applyFormula(Network network, String identifiableId, FormulaInfos formulaInfos) { + private void applyFormula(Network network, String identifiableId, FormulaInfos formulaInfos, Reporter subReporter) { Identifiable identifiable = network.getIdentifiable(identifiableId); Double value1 = formulaInfos.getFieldOrValue1().getRefOrValue(identifiable); Double value2 = formulaInfos.getFieldOrValue2().getRefOrValue(identifiable); @@ -100,6 +103,13 @@ private void applyFormula(Network network, String identifiableId, FormulaInfos f newValue); default -> throw new NetworkModificationException(NetworkModificationException.Type.WRONG_EQUIPMENT_TYPE, "Unsupported equipment"); } + + createReport(subReporter, "byFormulaModificationFormula", + String.format("successful application of new modification by formula on %s for %s %S", + formulaInfos.getEditedField(), + identifiable.getType(), + identifiable.getId()), + TypedValue.INFO_SEVERITY); } private Double applyOperation(Operator operator, Double value1, Double value2) { diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index b5fffb7d0..f768c3ae4 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -204,6 +204,6 @@ databaseChangeLog: - include: file: changesets/changelog_20231026T111001Z.xml relativeToChangelogFile: true - -include: + - include: file: changesets/changelog_20231011T120303Z.xml relativeToChangelogFile: true 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 4833a7864..f044feb50 100644 --- a/src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java +++ b/src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java @@ -7,7 +7,6 @@ package org.gridsuite.modification.server.modifications; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.matching.StringValuePattern; @@ -21,8 +20,6 @@ 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.BatteryField; -import org.gridsuite.modification.server.dto.formula.equipmentfield.GeneratorField; import org.gridsuite.modification.server.service.FilterService; import org.gridsuite.modification.server.utils.NetworkCreation; import org.junit.Before; @@ -38,7 +35,6 @@ import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -88,74 +84,8 @@ public void testCopy() throws Exception { wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), filters.stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false); } - @Test - public void testCreateWithError() throws Exception { - UUID filterId = UUID.randomUUID(); - FilterEquipments filter = getFilterEquipments(filterId, "filterWithWrongId", List.of(), List.of("wrongId1", "wrongId2")); - var filterInfo = FilterInfos.builder() - .id(filterId) - .name("filterWithWrongId") - .build(); - - UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/filters/export\\?networkUuid=" + getNetworkUuid() + "&variantId=variant_1&ids=" + filterId)) - .willReturn(WireMock.ok() - .withBody(mapper.writeValueAsString(List.of(filter))) - .withHeader("Content-Type", "application/json"))).getId(); - - FormulaInfos formulaInfos = FormulaInfos.builder() - .filters(List.of(filterInfo)) - .editedField(GeneratorField.ACTIVE_POWER_SET_POINT.name()) - .fieldOrValue1(ReferenceFieldOrValue.builder().value(55.).build()) - .operator(Operator.ADDITION) - .fieldOrValue2(ReferenceFieldOrValue.builder().value(20.).build()) - .build(); - - ByFormulaModificationInfos byFormulaModificationInfos = ByFormulaModificationInfos.builder() - .formulaInfosList(List.of(formulaInfos)) - .identifiableType(IdentifiableType.GENERATOR) - .build(); - - String modificationToCreateJson = mapper.writeValueAsString(byFormulaModificationInfos); - - MvcResult mvcResult = mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson).contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()).andReturn(); - - Optional networkModificationResult = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { }); - assertTrue(networkModificationResult.isPresent()); - assertEquals(NetworkModificationResult.ApplicationStatus.WITH_ERRORS, networkModificationResult.get().getApplicationStatus()); - - wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), List.of(filterId)), false); - } - - @Test - public void testCreateWithWarning() throws Exception { - UUID filterId = UUID.randomUUID(); - String equipmentId = "v3Battery"; - IdentifiableAttributes identifiableAttributes1 = getIdentifiableAttributes(equipmentId, 1.0); - FilterEquipments filter = getFilterEquipments(filterId, "filterWithWrongId", List.of(identifiableAttributes1), List.of("wrongId")); - var filterInfo = FilterInfos.builder() - .id(filterId) - .name("filterWithWrongId") - .build(); - - UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/filters/export\\?networkUuid=" + getNetworkUuid() + "&variantId=variant_1&ids=" + filterId)) - .willReturn(WireMock.ok() - .withBody(mapper.writeValueAsString(List.of(filter))) - .withHeader("Content-Type", "application/json"))).getId(); - - FormulaInfos formulaInfos = FormulaInfos.builder() - .filters(List.of(filterInfo)) - .editedField(BatteryField.ACTIVE_POWER_SET_POINT.name()) - .fieldOrValue1(ReferenceFieldOrValue.builder().value(55.).build()) - .operator(Operator.ADDITION) - .fieldOrValue2(ReferenceFieldOrValue.builder().value(20.).build()) - .build(); - - ByFormulaModificationInfos byFormulaModificationInfos = ByFormulaModificationInfos.builder() - .formulaInfosList(List.of(formulaInfos)) - .identifiableType(IdentifiableType.BATTERY) - .build(); - + void checkCreationApplicationStatus(ByFormulaModificationInfos byFormulaModificationInfos, + NetworkModificationResult.ApplicationStatus withErrors) throws Exception { String modificationToCreateJson = mapper.writeValueAsString(byFormulaModificationInfos); MvcResult mvcResult = mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson).contentType(MediaType.APPLICATION_JSON)) @@ -163,9 +93,7 @@ public void testCreateWithWarning() throws Exception { Optional networkModificationResult = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { }); assertTrue(networkModificationResult.isPresent()); - assertEquals(NetworkModificationResult.ApplicationStatus.WITH_WARNINGS, networkModificationResult.get().getApplicationStatus()); - - wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), List.of(filterId)), false); + assertEquals(withErrors, networkModificationResult.get().getApplicationStatus()); } @Override @@ -220,7 +148,7 @@ 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(",")))); } diff --git a/src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java index add41fa6e..ca5a2bab4 100644 --- a/src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java +++ b/src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java @@ -7,16 +7,20 @@ package org.gridsuite.modification.server.modifications; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.powsybl.iidm.network.IdentifiableType; import com.powsybl.iidm.network.extensions.ActivePowerControl; import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; +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.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.BatteryField; -import org.gridsuite.modification.server.dto.formula.equipmentfield.GeneratorField; +import org.junit.Test; import java.util.List; import java.util.UUID; @@ -42,6 +46,73 @@ public class BatteryByFormulaModificationTest extends AbstractByFormulaModificat private static final String BATTERY_ID_5 = "battery5"; private static final String BATTERY_ID_6 = "battery6"; + @Test + public void testCreateWithWarning() throws Exception { + UUID filterId = UUID.randomUUID(); + String equipmentId = "v3Battery"; + IdentifiableAttributes identifiableAttributes1 = getIdentifiableAttributes(equipmentId, 1.0); + FilterEquipments filter = getFilterEquipments(filterId, "filterWithWrongId", List.of(identifiableAttributes1), List.of("wrongId")); + var filterInfo = FilterInfos.builder() + .id(filterId) + .name("filterWithWrongId") + .build(); + + UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/filters/export\\?networkUuid=" + getNetworkUuid() + "&variantId=variant_1&ids=" + filterId)) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(List.of(filter))) + .withHeader("Content-Type", "application/json"))).getId(); + + FormulaInfos formulaInfos = FormulaInfos.builder() + .filters(List.of(filterInfo)) + .editedField(BatteryField.ACTIVE_POWER_SET_POINT.name()) + .fieldOrValue1(ReferenceFieldOrValue.builder().value(55.).build()) + .operator(Operator.ADDITION) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(20.).build()) + .build(); + + ByFormulaModificationInfos byFormulaModificationInfos = ByFormulaModificationInfos.builder() + .formulaInfosList(List.of(formulaInfos)) + .identifiableType(IdentifiableType.BATTERY) + .build(); + + checkCreationApplicationStatus(byFormulaModificationInfos, NetworkModificationResult.ApplicationStatus.WITH_WARNINGS); + assertEquals(75, getNetwork().getBattery(equipmentId).getTargetP(), 0); + + wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), List.of(filterId)), false); + } + + @Test + public void testCreateWithError() throws Exception { + UUID filterId = UUID.randomUUID(); + FilterEquipments filter = getFilterEquipments(filterId, "filterWithWrongId", List.of(), List.of("wrongId1", "wrongId2")); + var filterInfo = FilterInfos.builder() + .id(filterId) + .name("filterWithWrongId") + .build(); + + UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/filters/export\\?networkUuid=" + getNetworkUuid() + "&variantId=variant_1&ids=" + filterId)) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(List.of(filter))) + .withHeader("Content-Type", "application/json"))).getId(); + + FormulaInfos formulaInfos = FormulaInfos.builder() + .filters(List.of(filterInfo)) + .editedField(BatteryField.ACTIVE_POWER_SET_POINT.name()) + .fieldOrValue1(ReferenceFieldOrValue.builder().value(55.).build()) + .operator(Operator.ADDITION) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(20.).build()) + .build(); + + ByFormulaModificationInfos byFormulaModificationInfos = ByFormulaModificationInfos.builder() + .formulaInfosList(List.of(formulaInfos)) + .identifiableType(IdentifiableType.BATTERY) + .build(); + + checkCreationApplicationStatus(byFormulaModificationInfos, NetworkModificationResult.ApplicationStatus.WITH_ERRORS); + + wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), List.of(filterId)), false); + } + @Override void createEquipments() { getNetwork().getVariantManager().setWorkingVariant("variant_1"); @@ -104,8 +175,8 @@ List getFormulaInfos() { .name("filter5") .build(); - ReferenceFieldOrValue maxActivePowerRef = ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MAXIMUM_ACTIVE_POWER.name()).build(); - ReferenceFieldOrValue minActivePowerRef = ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MINIMUM_ACTIVE_POWER.name()).build(); + ReferenceFieldOrValue maxActivePowerRef = ReferenceFieldOrValue.builder().equipmentField(BatteryField.MAXIMUM_ACTIVE_POWER.name()).build(); + ReferenceFieldOrValue minActivePowerRef = ReferenceFieldOrValue.builder().equipmentField(BatteryField.MINIMUM_ACTIVE_POWER.name()).build(); FormulaInfos formulaInfos1 = getFormulaInfo(BatteryField.MAXIMUM_ACTIVE_POWER.name(), List.of(filter1, filter2), @@ -128,13 +199,13 @@ List getFormulaInfos() { FormulaInfos formulaInfos4 = getFormulaInfo(BatteryField.REACTIVE_POWER_SET_POINT.name(), List.of(filter4), Operator.DIVISION, - ReferenceFieldOrValue.builder().equipmentField(GeneratorField.REACTIVE_POWER_SET_POINT.name()).build(), + ReferenceFieldOrValue.builder().equipmentField(BatteryField.REACTIVE_POWER_SET_POINT.name()).build(), ReferenceFieldOrValue.builder().value(2.).build()); FormulaInfos formulaInfos5 = getFormulaInfo(BatteryField.DROOP.name(), List.of(filter4), Operator.MULTIPLICATION, - ReferenceFieldOrValue.builder().equipmentField(GeneratorField.DROOP.name()).build(), + ReferenceFieldOrValue.builder().equipmentField(BatteryField.DROOP.name()).build(), ReferenceFieldOrValue.builder().value(2.).build()); return List.of(formulaInfos1, formulaInfos2, formulaInfos3, formulaInfos4, formulaInfos5); @@ -170,14 +241,14 @@ List getUpdatedFormulaInfos() { FormulaInfos formulaInfos1 = FormulaInfos.builder() .editedField(BatteryField.MAXIMUM_ACTIVE_POWER.name()) .fieldOrValue1(ReferenceFieldOrValue.builder().value(200.).build()) - .fieldOrValue2(ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MAXIMUM_ACTIVE_POWER.name()).build()) + .fieldOrValue2(ReferenceFieldOrValue.builder().equipmentField(BatteryField.MAXIMUM_ACTIVE_POWER.name()).build()) .operator(Operator.ADDITION) .filters(List.of(filter1, filter2)) .build(); FormulaInfos formulaInfos2 = FormulaInfos.builder() .editedField(BatteryField.MINIMUM_ACTIVE_POWER.name()) - .fieldOrValue1(ReferenceFieldOrValue.builder().equipmentField(GeneratorField.MINIMUM_ACTIVE_POWER.name()).build()) + .fieldOrValue1(ReferenceFieldOrValue.builder().equipmentField(BatteryField.MINIMUM_ACTIVE_POWER.name()).build()) .fieldOrValue2(ReferenceFieldOrValue.builder().value(35.).build()) .operator(Operator.MODULUS) .filters(List.of(filter3)) @@ -185,7 +256,7 @@ List getUpdatedFormulaInfos() { FormulaInfos formulaInfos3 = FormulaInfos.builder() .editedField(BatteryField.ACTIVE_POWER_SET_POINT.name()) - .fieldOrValue1(ReferenceFieldOrValue.builder().equipmentField(GeneratorField.ACTIVE_POWER_SET_POINT.name()).build()) + .fieldOrValue1(ReferenceFieldOrValue.builder().equipmentField(BatteryField.ACTIVE_POWER_SET_POINT.name()).build()) .fieldOrValue2(ReferenceFieldOrValue.builder().value(10.).build()) .operator(Operator.ADDITION) .filters(List.of(filter5)) diff --git a/src/test/java/org/gridsuite/modification/server/modifications/GeneratorByFormulaModificationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/GeneratorByFormulaModificationTest.java index d8a438bc9..b6130fcd1 100644 --- a/src/test/java/org/gridsuite/modification/server/modifications/GeneratorByFormulaModificationTest.java +++ b/src/test/java/org/gridsuite/modification/server/modifications/GeneratorByFormulaModificationTest.java @@ -7,7 +7,9 @@ package org.gridsuite.modification.server.modifications; +import com.github.tomakehurst.wiremock.client.WireMock; import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.IdentifiableType; import com.powsybl.iidm.network.extensions.ActivePowerControl; import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; import com.powsybl.iidm.network.extensions.ConnectablePosition; @@ -17,13 +19,16 @@ import com.powsybl.iidm.network.extensions.GeneratorShortCircuitAdder; import com.powsybl.iidm.network.extensions.GeneratorStartup; import com.powsybl.iidm.network.extensions.GeneratorStartupAdder; +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.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.GeneratorField; +import org.junit.Test; import org.junit.jupiter.api.Tag; import java.util.List; @@ -55,6 +60,73 @@ public class GeneratorByFormulaModificationTest extends AbstractByFormulaModific private static final String GENERATOR_ID_9 = "gen9"; private static final String GENERATOR_ID_10 = "gen10"; + @Test + public void testCreateWithWarning() throws Exception { + UUID filterId = UUID.randomUUID(); + String equipmentId = "idGenerator"; + IdentifiableAttributes identifiableAttributes1 = getIdentifiableAttributes(equipmentId, 1.0); + FilterEquipments filter = getFilterEquipments(filterId, "filterWithWrongId", List.of(identifiableAttributes1), List.of("wrongId")); + var filterInfo = FilterInfos.builder() + .id(filterId) + .name("filterWithWrongId") + .build(); + + UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/filters/export\\?networkUuid=" + getNetworkUuid() + "&variantId=variant_1&ids=" + filterId)) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(List.of(filter))) + .withHeader("Content-Type", "application/json"))).getId(); + + FormulaInfos formulaInfos = FormulaInfos.builder() + .filters(List.of(filterInfo)) + .editedField(GeneratorField.ACTIVE_POWER_SET_POINT.name()) + .fieldOrValue1(ReferenceFieldOrValue.builder().value(55.).build()) + .operator(Operator.ADDITION) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(20.).build()) + .build(); + + ByFormulaModificationInfos byFormulaModificationInfos = ByFormulaModificationInfos.builder() + .formulaInfosList(List.of(formulaInfos)) + .identifiableType(IdentifiableType.GENERATOR) + .build(); + + checkCreationApplicationStatus(byFormulaModificationInfos, NetworkModificationResult.ApplicationStatus.WITH_WARNINGS); + assertEquals(75, getNetwork().getGenerator(equipmentId).getTargetP(), 0); + + wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), List.of(filterId)), false); + } + + @Test + public void testCreateWithError() throws Exception { + UUID filterId = UUID.randomUUID(); + FilterEquipments filter = getFilterEquipments(filterId, "filterWithWrongId", List.of(), List.of("wrongId1", "wrongId2")); + var filterInfo = FilterInfos.builder() + .id(filterId) + .name("filterWithWrongId") + .build(); + + UUID stubId = wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/filters/export\\?networkUuid=" + getNetworkUuid() + "&variantId=variant_1&ids=" + filterId)) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(List.of(filter))) + .withHeader("Content-Type", "application/json"))).getId(); + + FormulaInfos formulaInfos = FormulaInfos.builder() + .filters(List.of(filterInfo)) + .editedField(GeneratorField.ACTIVE_POWER_SET_POINT.name()) + .fieldOrValue1(ReferenceFieldOrValue.builder().value(55.).build()) + .operator(Operator.ADDITION) + .fieldOrValue2(ReferenceFieldOrValue.builder().value(20.).build()) + .build(); + + ByFormulaModificationInfos byFormulaModificationInfos = ByFormulaModificationInfos.builder() + .formulaInfosList(List.of(formulaInfos)) + .identifiableType(IdentifiableType.GENERATOR) + .build(); + + checkCreationApplicationStatus(byFormulaModificationInfos, NetworkModificationResult.ApplicationStatus.WITH_ERRORS); + + wireMockUtils.verifyGetRequest(stubId, PATH, handleQueryParams(getNetworkUuid(), List.of(filterId)), false); + } + void createEquipments() { getNetwork().getVariantManager().setWorkingVariant("variant_1"); getNetwork().getGenerator(GENERATOR_ID_1) From a9178a710e1aa64bce107991ca954f6d045b7657 Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Mon, 30 Oct 2023 14:31:28 +0100 Subject: [PATCH 05/20] fix bug --- .../dto/ByFormulaModificationInfos.java | 2 +- .../modifications/ByFormulaModification.java | 28 +++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java index 26d20a166..41d6e600b 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java @@ -39,7 +39,7 @@ @Schema(description = "Modification by formula") public class ByFormulaModificationInfos extends ModificationInfos { @Schema(description = "Identifiable type") - private IdentifiableType identifiableType; + private IdentifiableType C; @Schema(description = "list of formulas") private List formulaInfosList; 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 13e4ff01d..95c7bd439 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -114,17 +114,21 @@ private void applyFormula(Network network, String identifiableId, FormulaInfos f private Double applyOperation(Operator operator, Double value1, Double value2) { if (value1 == null || - value2 == null || - value2 == 0 && operator == Operator.DIVISION) { - throw new UnsupportedOperationException("TODO"); - } - - return switch (operator) { - case ADDITION -> value1 + value2; - case SUBTRACTION -> value1 - value2; - case MULTIPLICATION -> value1 * value2; - case DIVISION -> value1 / value2; - case MODULUS -> value1 % value2; + value2 == null ) { + throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, "at least one of the value or referenced field is null"); + } 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 0 in one formula"); + } + yield value1 / value2; + } + case MODULUS -> value1 % value2; }; - } + }} } From 1ff63e3cd8facf7cd47842b868e3bb59a5b89b3d Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Mon, 30 Oct 2023 14:40:51 +0100 Subject: [PATCH 06/20] fixes --- .../server/dto/ByFormulaModificationInfos.java | 2 +- .../server/modifications/ByFormulaModification.java | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java index 41d6e600b..26d20a166 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java @@ -39,7 +39,7 @@ @Schema(description = "Modification by formula") public class ByFormulaModificationInfos extends ModificationInfos { @Schema(description = "Identifiable type") - private IdentifiableType C; + private IdentifiableType identifiableType; @Schema(description = "list of formulas") private List formulaInfosList; 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 95c7bd439..77feec412 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -114,7 +114,7 @@ private void applyFormula(Network network, String identifiableId, FormulaInfos f private Double applyOperation(Operator operator, Double value1, Double value2) { if (value1 == null || - value2 == null ) { + value2 == null) { throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, "at least one of the value or referenced field is null"); } else { return switch (operator) { @@ -129,6 +129,7 @@ private Double applyOperation(Operator operator, Double value1, Double value2) { yield value1 / value2; } case MODULUS -> value1 % value2; - }; - }} + }; + } + } } From 09dc34ceec2319a6eac4f47a740ecd6038e0cf73 Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Mon, 30 Oct 2023 14:43:16 +0100 Subject: [PATCH 07/20] fixes --- .../server/modifications/ByFormulaModification.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 77feec412..6d4597f10 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -125,8 +125,9 @@ private Double applyOperation(Operator operator, Double value1, Double value2) { if (value2 == 0) { throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, "there is a division by 0 in one formula"); + } else { + yield value1 / value2; } - yield value1 / value2; } case MODULUS -> value1 % value2; }; From 78d65d2478ef16ca0f5b72e7f20aab99d1fbfdaf Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Thu, 2 Nov 2023 10:24:19 +0100 Subject: [PATCH 08/20] fixes --- .../dto/ByFormulaModificationInfos.java | 2 ++ .../server/dto/formula/FormulaInfos.java | 4 +++ .../ByFormulaModificationEntity.java | 14 +++++++--- .../equipment/modification/FormulaEntity.java | 28 +++++++++++++++++-- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java index 26d20a166..9d21feb87 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/ByFormulaModificationInfos.java @@ -16,6 +16,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import lombok.ToString; import lombok.experimental.SuperBuilder; import org.gridsuite.modification.server.ModificationType; import org.gridsuite.modification.server.dto.annotation.ModificationErrorTypeName; @@ -36,6 +37,7 @@ @Setter @JsonTypeName("BY_FORMULA_MODIFICATION") @ModificationErrorTypeName("BY_FORMULA_MODIFICATION_ERROR") +@ToString(callSuper = true) @Schema(description = "Modification by formula") public class ByFormulaModificationInfos extends ModificationInfos { @Schema(description = "Identifiable type") diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java b/src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java index 65079e515..736556a4d 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/FormulaInfos.java @@ -17,6 +17,7 @@ import org.gridsuite.modification.server.entities.equipment.modification.FormulaEntity; import java.util.List; +import java.util.UUID; /** * @author Seddik Yengui @@ -28,6 +29,9 @@ @Getter @Setter public class FormulaInfos { + @Schema(description = "id") + private UUID id; + @Schema(description = "List of filters") private List filters; diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java index fee797da7..64770b5b6 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java @@ -10,6 +10,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.NonNull; +import lombok.Setter; import org.gridsuite.modification.server.dto.ByFormulaModificationInfos; import org.gridsuite.modification.server.dto.ModificationInfos; import org.gridsuite.modification.server.dto.formula.FormulaInfos; @@ -20,6 +21,7 @@ @NoArgsConstructor @Getter +@Setter @Entity @Table(name = "byFormulaModification") public class ByFormulaModificationEntity extends ModificationEntity { @@ -42,13 +44,17 @@ public void update(@NonNull ModificationInfos modificationInfos) { private void assignAttributes(ByFormulaModificationInfos byFormulaModificationInfos) { this.identifiableType = byFormulaModificationInfos.getIdentifiableType(); - List formulas = byFormulaModificationInfos.getFormulaInfosList().stream() - .map(FormulaInfos::toEntity).toList(); if (formulaEntities == null) { - formulaEntities = formulas; + formulaEntities = byFormulaModificationInfos.getFormulaInfosList() + .stream() + .map(FormulaInfos::toEntity) + .toList(); } else { formulaEntities.clear(); - formulaEntities.addAll(formulas); + formulaEntities.addAll(byFormulaModificationInfos.getFormulaInfosList() + .stream() + .map(FormulaInfos::toEntity) + .toList()); } } diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java index 2ab805aa5..341c0c9c6 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java @@ -1,8 +1,16 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + package org.gridsuite.modification.server.entities.equipment.modification; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; import org.gridsuite.modification.server.dto.FilterInfos; import org.gridsuite.modification.server.dto.formula.FormulaInfos; import org.gridsuite.modification.server.dto.formula.Operator; @@ -12,8 +20,13 @@ import java.util.UUID; import java.util.stream.Collectors; +/** + * @author Seddik Yengui + */ + @NoArgsConstructor @Getter +@Setter @Entity @Table(name = "formula") public class FormulaEntity { @@ -47,7 +60,13 @@ public class FormulaEntity { private Operator operator; public FormulaEntity(FormulaInfos formulaInfos) { - this.filters = formulaInfos.getFilters().stream().map(FilterInfos::toEntity).collect(Collectors.toList()); + this.id = null; + if (filters == null) { + this.filters = formulaInfos.getFilters().stream().map(FilterInfos::toEntity).collect(Collectors.toList()); + } else { + filters.clear(); + filters.addAll(formulaInfos.getFilters().stream().map(FilterInfos::toEntity).collect(Collectors.toList())); + } this.editedField = formulaInfos.getEditedField(); this.equipmentField1 = formulaInfos.getFieldOrValue1().getEquipmentField(); this.equipmentField2 = formulaInfos.getFieldOrValue2().getEquipmentField(); @@ -58,15 +77,18 @@ public FormulaEntity(FormulaInfos formulaInfos) { public FormulaInfos toFormulaInfos() { return FormulaInfos.builder() + .id(getId()) .filters(getFilters().stream() .map(filterEntity -> new FilterInfos(filterEntity.getFilterId(), filterEntity.getName())) .collect(Collectors.toList())) .editedField(getEditedField()) .fieldOrValue1(ReferenceFieldOrValue.builder() - .equipmentField(getEquipmentField1()).value(getValue1()) + .equipmentField(getEquipmentField1()) + .value(getValue1()) .build()) .fieldOrValue2(ReferenceFieldOrValue.builder() - .equipmentField(getEquipmentField2()).value(getValue2()) + .equipmentField(getEquipmentField2()) + .value(getValue2()) .build()) .operator(getOperator()) .build(); From e308b570fedafdc27e1aa12b01fadb26b19b2bf7 Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Thu, 2 Nov 2023 16:35:11 +0100 Subject: [PATCH 09/20] fixes formula orders --- .../ByFormulaModificationEntity.java | 9 +-- .../equipment/modification/FormulaEntity.java | 5 +- .../changesets/changelog_20231026T111001Z.xml | 65 ------------------- .../changesets/changelog_20231102T152406Z.xml | 43 ++++++++++++ .../db/changelog/db.changelog-master.yaml | 6 +- 5 files changed, 50 insertions(+), 78 deletions(-) delete mode 100644 src/main/resources/db/changelog/changesets/changelog_20231026T111001Z.xml create mode 100644 src/main/resources/db/changelog/changesets/changelog_20231102T152406Z.xml diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java index 64770b5b6..6b0a945d7 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/ByFormulaModificationEntity.java @@ -1,12 +1,7 @@ package org.gridsuite.modification.server.entities.equipment.modification; import com.powsybl.iidm.network.IdentifiableType; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; +import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.NonNull; @@ -29,6 +24,8 @@ public class ByFormulaModificationEntity extends ModificationEntity { private IdentifiableType identifiableType; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @JoinColumn(name = "by_formula_modification_id", + foreignKey = @ForeignKey(name = "by_formula_modification_id_fk")) private List formulaEntities; public ByFormulaModificationEntity(ByFormulaModificationInfos byFormulaModificationInfos) { diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java index 341c0c9c6..c7c1ccce8 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java @@ -28,7 +28,7 @@ @Getter @Setter @Entity -@Table(name = "formula") +@Table(name = "formula", indexes = @Index(name = "by_formula_modification_id_idx", columnList = "by_formula_modification_id")) public class FormulaEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) @@ -36,9 +36,6 @@ public class FormulaEntity { private UUID id; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) - @JoinTable( - joinColumns = @JoinColumn(name = "id"), - inverseJoinColumns = @JoinColumn(name = "filterId")) private List filters; @Column diff --git a/src/main/resources/db/changelog/changesets/changelog_20231026T111001Z.xml b/src/main/resources/db/changelog/changesets/changelog_20231026T111001Z.xml deleted file mode 100644 index 682a02759..000000000 --- a/src/main/resources/db/changelog/changesets/changelog_20231026T111001Z.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/db/changelog/changesets/changelog_20231102T152406Z.xml b/src/main/resources/db/changelog/changesets/changelog_20231102T152406Z.xml new file mode 100644 index 000000000..73c3b3cce --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20231102T152406Z.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index c9d00f994..27e59811b 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -201,12 +201,12 @@ databaseChangeLog: - include: file: changesets/changelog_20230801T194601Z.xml relativeToChangelogFile: true - - include: - file: changesets/changelog_20231026T111001Z.xml - relativeToChangelogFile: true - include: file: changesets/changelog_20231011T120303Z.xml relativeToChangelogFile: true - include: file: changesets/changelog_20231019T143032Z.xml relativeToChangelogFile: true + - include: + file: changesets/changelog_20231102T152406Z.xml + relativeToChangelogFile: true From cfd1387a9810b0832f2ea1976359b7ac29c7ffee Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Fri, 3 Nov 2023 11:36:27 +0100 Subject: [PATCH 10/20] add modif check & fix reports --- .../equipment/modification/FormulaEntity.java | 2 + .../modification/VariationFilterEntity.java | 3 +- .../modifications/ByFormulaModification.java | 96 ++++++++++--------- .../modifications/ModificationUtils.java | 31 ++++++ .../server/service/FilterService.java | 15 +++ ...06Z.xml => changelog_20231102T173618Z.xml} | 36 ++++--- .../db/changelog/db.changelog-master.yaml | 2 +- 7 files changed, 124 insertions(+), 61 deletions(-) rename src/main/resources/db/changelog/changesets/{changelog_20231102T152406Z.xml => changelog_20231102T173618Z.xml} (53%) diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java index c7c1ccce8..b11b7aca8 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java @@ -36,6 +36,8 @@ public class FormulaEntity { private UUID id; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @JoinColumn(name = "formula_id", + foreignKey = @ForeignKey(name = "formula_id_fk")) private List filters; @Column diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/VariationFilterEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/VariationFilterEntity.java index 5d5c8ee79..ee9e4c15f 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/VariationFilterEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/VariationFilterEntity.java @@ -6,6 +6,7 @@ */ package org.gridsuite.modification.server.entities.equipment.modification; +import jakarta.persistence.Index; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -27,7 +28,7 @@ @Getter @Setter @Entity -@Table(name = "VariationFilter") +@Table(name = "VariationFilter", indexes = @Index(name = "formula_id_idx", columnList = "formula_id")) public class VariationFilterEntity { @Id 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 6d4597f10..c52a79da7 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -1,12 +1,12 @@ package org.gridsuite.modification.server.modifications; +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.Network; -import com.powsybl.network.store.iidm.impl.NetworkImpl; import org.gridsuite.modification.server.NetworkModificationException; import org.gridsuite.modification.server.dto.ByFormulaModificationInfos; import org.gridsuite.modification.server.dto.FilterEquipments; @@ -16,12 +16,13 @@ import org.gridsuite.modification.server.dto.formula.equipmentfield.BatteryField; import org.gridsuite.modification.server.dto.formula.equipmentfield.GeneratorField; import org.gridsuite.modification.server.service.FilterService; +import org.jetbrains.annotations.Nullable; import org.springframework.util.CollectionUtils; import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.function.Function; import java.util.stream.Collectors; import static org.gridsuite.modification.server.modifications.ModificationUtils.createReport; @@ -40,6 +41,21 @@ public void initApplicationContext(NetworkModificationApplicator modificationApp filterService = modificationApplicator.getFilterService(); } + @Override + public void check(Network network) throws NetworkModificationException { + if (modificationInfos == null) { + throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, "Missing required attributes to modify the equipment"); + } + + if (CollectionUtils.isEmpty(modificationInfos.getFormulaInfosList())) { + throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, "At least one formula is required"); + } + + if (modificationInfos.getFormulaInfosList().stream().anyMatch(formulaInfos -> CollectionUtils.isEmpty(formulaInfos.getFilters()))) { + throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, "Every formula must have at least one filter"); + } + } + @Override public void apply(Network network, Reporter subReporter) { // collect all filters from all variations @@ -48,48 +64,38 @@ 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 = modificationInfos.getErrorType() + ": There is no valid equipment ID among the provided filter(s)"; - createReport(subReporter, "invalidFilters", errorMsg, TypedValue.ERROR_SEVERITY); - return; + Map exportFilters = getUuidFilterEquipmentsMap(network, subReporter, filters); + + if (exportFilters != null) { + List formulaReports = new ArrayList<>(); + modificationInfos.getFormulaInfosList().forEach(formulaInfos -> formulaInfos.getFilters().forEach(filterInfos -> { + var filterEquipments = exportFilters.get(filterInfos.getId()); + filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, + attributes.getId(), + formulaInfos, formulaReports)); + })); + + createReport(subReporter, "byFormulaModification", "new modification by formula", TypedValue.INFO_SEVERITY); + ModificationUtils.getInstance().reportModifications(subReporter, + formulaReports, + "appliedFormulasModifications", + "Formulas : "); } + } - // 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); - }); - - modificationInfos.getFormulaInfosList().forEach(formulaInfos -> formulaInfos.getFilters().forEach(filterInfos -> { - var filterEquipments = exportFilters.get(filterInfos.getId()); - filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, attributes.getId(), formulaInfos, subReporter)); - })); - - createReport(subReporter, "byFormulaModification", "new modification by formula", TypedValue.INFO_SEVERITY); + @Nullable + private Map getUuidFilterEquipmentsMap(Network network, Reporter subReporter, Map filters) { + // export filters from filter server + Map exportFilters = filterService.getUuidFilterEquipmentsMap(network, filters); + boolean isValidFilter = ModificationUtils.getInstance().isValidFilter(subReporter, modificationInfos.getErrorType(), filters, exportFilters); + return isValidFilter ? exportFilters : null; } - private void applyFormula(Network network, String identifiableId, FormulaInfos formulaInfos, Reporter subReporter) { + private void applyFormula(Network network, + String identifiableId, + FormulaInfos formulaInfos, + List formulaReports) { Identifiable identifiable = network.getIdentifiable(identifiableId); Double value1 = formulaInfos.getFieldOrValue1().getRefOrValue(identifiable); Double value2 = formulaInfos.getFieldOrValue2().getRefOrValue(identifiable); @@ -101,15 +107,17 @@ private void applyFormula(Network network, String identifiableId, FormulaInfos f case BATTERY -> BatteryField.setNewValue((Battery) identifiable, formulaInfos.getEditedField(), newValue); - default -> throw new NetworkModificationException(NetworkModificationException.Type.WRONG_EQUIPMENT_TYPE, "Unsupported equipment"); + default -> throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, "Unsupported equipment"); } - createReport(subReporter, "byFormulaModificationFormula", - String.format("successful application of new modification by formula on %s for %s %S", + formulaReports.add(Report.builder() + .withKey("byFormulaModificationFormula" + formulaReports.size()) + .withDefaultMessage(String.format("successful application of new modification by formula on %s for %s %s", formulaInfos.getEditedField(), identifiable.getType(), - identifiable.getId()), - TypedValue.INFO_SEVERITY); + identifiable.getId())) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); } private Double applyOperation(Operator operator, Double value1, Double value2) { 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 fefba42e9..fe50cbf10 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java @@ -29,6 +29,7 @@ import java.util.function.Predicate; import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.stream.Collectors; import java.util.stream.IntStream; import static org.gridsuite.modification.server.NetworkModificationException.Type.*; @@ -961,5 +962,35 @@ private void createReactiveCapabilityCurvePoint(ReactiveCapabilityCurveAdder add addToReports(reports, point.getQminP(), "QminP" + fieldSuffix); addToReports(reports, point.getQmaxP(), "QmaxP" + fieldSuffix); } + + public boolean isValidFilter(Reporter subReporter, + NetworkModificationException.Type errorType, + Map filters, + Map exportFilters) { + // 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 = errorType + ": There is no valid equipment ID among the provided filter(s)"; + createReport(subReporter, "invalidFilters", errorMsg, TypedValue.ERROR_SEVERITY); + return false; + } + + // 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 true; + } } diff --git a/src/main/java/org/gridsuite/modification/server/service/FilterService.java b/src/main/java/org/gridsuite/modification/server/service/FilterService.java index 1760b6b94..38eca8874 100644 --- a/src/main/java/org/gridsuite/modification/server/service/FilterService.java +++ b/src/main/java/org/gridsuite/modification/server/service/FilterService.java @@ -9,6 +9,9 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.powsybl.commons.reporter.Reporter; +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.FilterEquipments; import org.slf4j.Logger; @@ -21,8 +24,11 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import static org.gridsuite.modification.server.NetworkModificationException.Type.FILTERS_NOT_FOUND; @@ -65,6 +71,15 @@ public List exportFilters(List filtersUuids, UUID networ } } + public Map getUuidFilterEquipmentsMap(Network network, Map filters) { + String workingVariantId = network.getVariantManager().getWorkingVariantId(); + UUID uuid = ((NetworkImpl) network).getUuid(); + return exportFilters(new ArrayList<>(filters.keySet()), uuid, workingVariantId) + .stream() + .peek(t -> t.setFilterName(filters.get(t.getFilterId()))) + .collect(Collectors.toMap(FilterEquipments::getFilterId, Function.identity())); + } + private NetworkModificationException handleChangeError(HttpStatusCodeException httpException, NetworkModificationException.Type type) { String responseBody = httpException.getResponseBodyAsString(); if (responseBody.isEmpty()) { diff --git a/src/main/resources/db/changelog/changesets/changelog_20231102T152406Z.xml b/src/main/resources/db/changelog/changesets/changelog_20231102T173618Z.xml similarity index 53% rename from src/main/resources/db/changelog/changesets/changelog_20231102T152406Z.xml rename to src/main/resources/db/changelog/changesets/changelog_20231102T173618Z.xml index 73c3b3cce..f437cc472 100644 --- a/src/main/resources/db/changelog/changesets/changelog_20231102T152406Z.xml +++ b/src/main/resources/db/changelog/changesets/changelog_20231102T173618Z.xml @@ -1,6 +1,6 @@ - + @@ -8,7 +8,7 @@ - + @@ -22,22 +22,28 @@ - - - - - - - - - + + + + - - - - + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 27e59811b..0047b0cd9 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -208,5 +208,5 @@ databaseChangeLog: file: changesets/changelog_20231019T143032Z.xml relativeToChangelogFile: true - include: - file: changesets/changelog_20231102T152406Z.xml + file: changesets/changelog_20231102T173618Z.xml relativeToChangelogFile: true From 9ed52d8e90473e7c833dc73aa757236b2a60e988 Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Fri, 3 Nov 2023 11:39:32 +0100 Subject: [PATCH 11/20] fix checkstyle --- .../org/gridsuite/modification/server/service/FilterService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/gridsuite/modification/server/service/FilterService.java b/src/main/java/org/gridsuite/modification/server/service/FilterService.java index 38eca8874..f10ba4762 100644 --- a/src/main/java/org/gridsuite/modification/server/service/FilterService.java +++ b/src/main/java/org/gridsuite/modification/server/service/FilterService.java @@ -9,7 +9,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.powsybl.commons.reporter.Reporter; import com.powsybl.iidm.network.Network; import com.powsybl.network.store.iidm.impl.NetworkImpl; import org.gridsuite.modification.server.NetworkModificationException; From f59fc61032b46ed3245096d646909982a9ed9ebc Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Wed, 8 Nov 2023 14:01:39 +0100 Subject: [PATCH 12/20] review request --- .../equipment/modification/FormulaEntity.java | 7 +------ ...73618Z.xml => changelog_20231108T124514Z.xml} | 16 ++++++++-------- .../db/changelog/db.changelog-master.yaml | 2 +- 3 files changed, 10 insertions(+), 15 deletions(-) rename src/main/resources/db/changelog/changesets/{changelog_20231102T173618Z.xml => changelog_20231108T124514Z.xml} (85%) diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java index b11b7aca8..1ea147165 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/FormulaEntity.java @@ -60,12 +60,7 @@ public class FormulaEntity { public FormulaEntity(FormulaInfos formulaInfos) { this.id = null; - if (filters == null) { - this.filters = formulaInfos.getFilters().stream().map(FilterInfos::toEntity).collect(Collectors.toList()); - } else { - filters.clear(); - filters.addAll(formulaInfos.getFilters().stream().map(FilterInfos::toEntity).collect(Collectors.toList())); - } + this.filters = formulaInfos.getFilters().stream().map(FilterInfos::toEntity).collect(Collectors.toList()); this.editedField = formulaInfos.getEditedField(); this.equipmentField1 = formulaInfos.getFieldOrValue1().getEquipmentField(); this.equipmentField2 = formulaInfos.getFieldOrValue2().getEquipmentField(); diff --git a/src/main/resources/db/changelog/changesets/changelog_20231102T173618Z.xml b/src/main/resources/db/changelog/changesets/changelog_20231108T124514Z.xml similarity index 85% rename from src/main/resources/db/changelog/changesets/changelog_20231102T173618Z.xml rename to src/main/resources/db/changelog/changesets/changelog_20231108T124514Z.xml index f437cc472..5e86f9fe3 100644 --- a/src/main/resources/db/changelog/changesets/changelog_20231102T173618Z.xml +++ b/src/main/resources/db/changelog/changesets/changelog_20231108T124514Z.xml @@ -1,6 +1,6 @@ - + @@ -8,7 +8,7 @@ - + @@ -22,28 +22,28 @@ - + - + - + - + - + - + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index fd62e762c..cb927e908 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -211,5 +211,5 @@ databaseChangeLog: file: changesets/changelog_20231010T120459Z.xml relativeToChangelogFile: true - include: - file: changesets/changelog_20231102T173618Z.xml + file: changesets/changelog_20231108T124514Z.xml relativeToChangelogFile: true From 37a1fe46380cd6a38afb28589d6293eff0124cc4 Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Thu, 9 Nov 2023 01:47:43 +0100 Subject: [PATCH 13/20] fix logs --- .../modifications/ByFormulaModification.java | 74 +++++++++++++------ .../modifications/ModificationUtils.java | 8 -- 2 files changed, 52 insertions(+), 30 deletions(-) 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 c52a79da7..936b727a7 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -29,7 +29,7 @@ import static org.gridsuite.modification.server.modifications.ModificationUtils.distinctByKey; public class ByFormulaModification extends AbstractModification { - private ByFormulaModificationInfos modificationInfos; + private final ByFormulaModificationInfos modificationInfos; protected FilterService filterService; public ByFormulaModification(ByFormulaModificationInfos modificationInfos) { @@ -67,22 +67,62 @@ public void apply(Network network, Reporter subReporter) { Map exportFilters = getUuidFilterEquipmentsMap(network, subReporter, filters); if (exportFilters != null) { + Reporter formulaSubReporter = subReporter.createSubReporter("appliedFormulasModifications", "Formulas"); List formulaReports = new ArrayList<>(); - modificationInfos.getFormulaInfosList().forEach(formulaInfos -> formulaInfos.getFilters().forEach(filterInfos -> { - var filterEquipments = exportFilters.get(filterInfos.getId()); - filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, - attributes.getId(), - formulaInfos, formulaReports)); - })); + modificationInfos.getFormulaInfosList().forEach(formulaInfos -> + formulaInfos.getFilters().forEach(filterInfos -> + applyFormulaOnFilterEquipments(network, exportFilters, formulaReports, formulaInfos, filterInfos))); createReport(subReporter, "byFormulaModification", "new modification by formula", TypedValue.INFO_SEVERITY); - ModificationUtils.getInstance().reportModifications(subReporter, - formulaReports, - "appliedFormulasModifications", - "Formulas : "); + 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, + FormulaInfos formulaInfos, + FilterInfos filterInfos) { + var filterEquipments = exportFilters.get(filterInfos.getId()); + filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, + attributes.getId(), + formulaInfos)); + + 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())) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + + if (!CollectionUtils.isEmpty(filterEquipments.getNotFoundEquipments())) { + var equipmentIds = String.join(", ", filterEquipments.getNotFoundEquipments()); + formulaReports.add(Report.builder() + .withKey("filterEquipmentsNotFound_" + formulaReports.size()) + .withDefaultMessage(String.format(" Equipment not found : %s", + equipmentIds)) + .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()); + } + @Nullable private Map getUuidFilterEquipmentsMap(Network network, Reporter subReporter, Map filters) { // export filters from filter server @@ -94,8 +134,7 @@ private Map getUuidFilterEquipmentsMap(Network network, private void applyFormula(Network network, String identifiableId, - FormulaInfos formulaInfos, - List formulaReports) { + FormulaInfos formulaInfos) { Identifiable identifiable = network.getIdentifiable(identifiableId); Double value1 = formulaInfos.getFieldOrValue1().getRefOrValue(identifiable); Double value2 = formulaInfos.getFieldOrValue2().getRefOrValue(identifiable); @@ -109,15 +148,6 @@ private void applyFormula(Network network, newValue); default -> throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, "Unsupported equipment"); } - - formulaReports.add(Report.builder() - .withKey("byFormulaModificationFormula" + formulaReports.size()) - .withDefaultMessage(String.format("successful application of new modification by formula on %s for %s %s", - formulaInfos.getEditedField(), - identifiable.getType(), - identifiable.getId())) - .withSeverity(TypedValue.INFO_SEVERITY) - .build()); } private Double applyOperation(Operator operator, Double value1, Double value2) { 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 fe50cbf10..7b6e30709 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java @@ -982,14 +982,6 @@ public boolean isValidFilter(Reporter subReporter, return false; } - // 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 true; } } From d737c5cf4206727dbf1a7b2a847798eefa53bc9e Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Thu, 9 Nov 2023 15:36:26 +0100 Subject: [PATCH 14/20] add more specific logs --- .../server/dto/formula/Operator.java | 2 +- .../modifications/ByFormulaModification.java | 18 +++++++++++++--- .../AbstractByFormulaModificationTest.java | 4 ++-- .../BatteryByFormulaModificationTest.java | 21 +++++++------------ 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java b/src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java index e2ecf2926..d00f5e65d 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java +++ b/src/main/java/org/gridsuite/modification/server/dto/formula/Operator.java @@ -16,5 +16,5 @@ public enum Operator { SUBTRACTION, MULTIPLICATION, DIVISION, - MODULUS + PERCENTAGE } 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 936b727a7..45a139e53 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -91,7 +91,8 @@ private void applyFormulaOnFilterEquipments(Network network, var filterEquipments = exportFilters.get(filterInfos.getId()); filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, attributes.getId(), - formulaInfos)); + formulaInfos, + formulaReports)); formulaReports.add(Report.builder() .withKey("byFormulaModificationFormulaFilter_" + formulaReports.size()) @@ -134,7 +135,8 @@ private Map getUuidFilterEquipmentsMap(Network network, private void applyFormula(Network network, String identifiableId, - FormulaInfos formulaInfos) { + FormulaInfos formulaInfos, + List reports) { Identifiable identifiable = network.getIdentifiable(identifiableId); Double value1 = formulaInfos.getFieldOrValue1().getRefOrValue(identifiable); Double value2 = formulaInfos.getFieldOrValue2().getRefOrValue(identifiable); @@ -148,6 +150,16 @@ private void applyFormula(Network network, 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) { @@ -167,7 +179,7 @@ private Double applyOperation(Operator operator, Double value1, Double value2) { yield value1 / value2; } } - case MODULUS -> value1 % value2; + case PERCENTAGE -> value1 * (value2 / 100); }; } } 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 f044feb50..45e2b6fdf 100644 --- a/src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java +++ b/src/test/java/org/gridsuite/modification/server/modifications/AbstractByFormulaModificationTest.java @@ -85,7 +85,7 @@ public void testCopy() throws Exception { } void checkCreationApplicationStatus(ByFormulaModificationInfos byFormulaModificationInfos, - NetworkModificationResult.ApplicationStatus withErrors) throws Exception { + NetworkModificationResult.ApplicationStatus applicationStatus) throws Exception { String modificationToCreateJson = mapper.writeValueAsString(byFormulaModificationInfos); MvcResult mvcResult = mockMvc.perform(post(getNetworkModificationUri()).content(modificationToCreateJson).contentType(MediaType.APPLICATION_JSON)) @@ -93,7 +93,7 @@ void checkCreationApplicationStatus(ByFormulaModificationInfos byFormulaModifica Optional networkModificationResult = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { }); assertTrue(networkModificationResult.isPresent()); - assertEquals(withErrors, networkModificationResult.get().getApplicationStatus()); + assertEquals(applicationStatus, networkModificationResult.get().getApplicationStatus()); } @Override diff --git a/src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java index ca5a2bab4..9c88baf83 100644 --- a/src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java +++ b/src/test/java/org/gridsuite/modification/server/modifications/BatteryByFormulaModificationTest.java @@ -186,9 +186,9 @@ List getFormulaInfos() { FormulaInfos formulaInfos2 = getFormulaInfo(BatteryField.MINIMUM_ACTIVE_POWER.name(), List.of(filter3), - Operator.MODULUS, - minActivePowerRef, - ReferenceFieldOrValue.builder().value(30.).build()); + Operator.PERCENTAGE, + ReferenceFieldOrValue.builder().value(30.).build(), + minActivePowerRef); FormulaInfos formulaInfos3 = getFormulaInfo(BatteryField.ACTIVE_POWER_SET_POINT.name(), List.of(filter5), @@ -228,11 +228,6 @@ List getUpdatedFormulaInfos() { .name("filter3") .build(); - var filter4 = FilterInfos.builder() - .id(FILTER_ID_4) - .name("filter4") - .build(); - var filter5 = FilterInfos.builder() .id(FILTER_ID_5) .name("filter5") @@ -248,9 +243,9 @@ List getUpdatedFormulaInfos() { FormulaInfos formulaInfos2 = FormulaInfos.builder() .editedField(BatteryField.MINIMUM_ACTIVE_POWER.name()) - .fieldOrValue1(ReferenceFieldOrValue.builder().equipmentField(BatteryField.MINIMUM_ACTIVE_POWER.name()).build()) - .fieldOrValue2(ReferenceFieldOrValue.builder().value(35.).build()) - .operator(Operator.MODULUS) + .fieldOrValue1(ReferenceFieldOrValue.builder().value(35.).build()) + .fieldOrValue2(ReferenceFieldOrValue.builder().equipmentField(BatteryField.MINIMUM_ACTIVE_POWER.name()).build()) + .operator(Operator.PERCENTAGE) .filters(List.of(filter3)) .build(); @@ -279,13 +274,13 @@ protected void assertAfterNetworkModificationCreation() { assertEquals(380, getNetwork().getBattery(BATTERY_ID_3).getTargetP(), 0); assertEquals(400, getNetwork().getBattery(BATTERY_ID_4).getMaxP(), 0); - assertEquals(20, getNetwork().getBattery(BATTERY_ID_5).getMinP(), 0); + assertEquals(15, getNetwork().getBattery(BATTERY_ID_5).getMinP(), 0); assertEquals(70, getNetwork().getBattery(BATTERY_ID_5).getTargetQ(), 0); ActivePowerControl activePowerControl5 = getNetwork().getBattery(BATTERY_ID_5).getExtension(ActivePowerControl.class); assertNotNull(activePowerControl5); assertEquals(8, activePowerControl5.getDroop(), 0); - assertEquals(20, getNetwork().getBattery(BATTERY_ID_6).getMinP(), 0); + assertEquals(60, getNetwork().getBattery(BATTERY_ID_6).getMinP(), 0); } @Override From 0ff9855b7f88d8b06359ccf02501b463d84f20a7 Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Thu, 9 Nov 2023 15:58:08 +0100 Subject: [PATCH 15/20] logs order fix --- .../server/modifications/ByFormulaModification.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 45a139e53..eb1ecc8aa 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -89,10 +89,6 @@ private void applyFormulaOnFilterEquipments(Network network, FormulaInfos formulaInfos, FilterInfos filterInfos) { var filterEquipments = exportFilters.get(filterInfos.getId()); - filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, - attributes.getId(), - formulaInfos, - formulaReports)); formulaReports.add(Report.builder() .withKey("byFormulaModificationFormulaFilter_" + formulaReports.size()) @@ -122,6 +118,11 @@ private void applyFormulaOnFilterEquipments(Network network, .withDefaultMessage(String.format(" Edited field : %s", formulaInfos.getEditedField())) .withSeverity(TypedValue.INFO_SEVERITY) .build()); + + filterEquipments.getIdentifiableAttributes().forEach(attributes -> applyFormula(network, + attributes.getId(), + formulaInfos, + formulaReports)); } @Nullable From 7ac6a14191b361fdba37a8b04c516d402ee932d0 Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Thu, 9 Nov 2023 16:02:34 +0100 Subject: [PATCH 16/20] logs order fix --- .../server/modifications/ByFormulaModification.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 eb1ecc8aa..a6b0a140c 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -92,7 +92,7 @@ private void applyFormulaOnFilterEquipments(Network network, formulaReports.add(Report.builder() .withKey("byFormulaModificationFormulaFilter_" + formulaReports.size()) - .withDefaultMessage(String.format(" successful application of new modification by formula on filter %s", + .withDefaultMessage(String.format("Successful application of new modification by formula on filter %s", filterInfos.getName())) .withSeverity(TypedValue.INFO_SEVERITY) .build()); From c7fda0400502ac749439231caf8cb3e690b13fb3 Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Fri, 10 Nov 2023 11:26:05 +0100 Subject: [PATCH 17/20] remove unused variable --- .../server/modifications/ByFormulaModification.java | 2 +- .../server/modifications/ModificationUtils.java | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) 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 a6b0a140c..4ce9c2f63 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -130,7 +130,7 @@ private Map getUuidFilterEquipmentsMap(Network network, // export filters from filter server Map exportFilters = filterService.getUuidFilterEquipmentsMap(network, filters); - boolean isValidFilter = ModificationUtils.getInstance().isValidFilter(subReporter, modificationInfos.getErrorType(), filters, exportFilters); + boolean isValidFilter = ModificationUtils.getInstance().isValidFilter(subReporter, modificationInfos.getErrorType(), exportFilters); return isValidFilter ? exportFilters : 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 ffd2a0977..ac4e423bc 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java @@ -29,7 +29,6 @@ import java.util.function.Predicate; import java.util.function.Consumer; import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.IntStream; import static org.gridsuite.modification.server.NetworkModificationException.Type.*; @@ -978,14 +977,7 @@ private void createReactiveCapabilityCurvePoint(ReactiveCapabilityCurveAdder add public boolean isValidFilter(Reporter subReporter, NetworkModificationException.Type errorType, - Map filters, Map exportFilters) { - // 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()); From 56074a0c3c1d145c945c2092e3b5ebad3c69cea9 Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Fri, 10 Nov 2023 14:05:29 +0100 Subject: [PATCH 18/20] fix --- .../server/modifications/ByFormulaModification.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4ce9c2f63..b6e4ca235 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -175,7 +175,7 @@ private Double applyOperation(Operator operator, Double value1, Double value2) { case DIVISION -> { if (value2 == 0) { throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, - "there is a division by 0 in one formula"); + "there is a division by zero in one formula"); } else { yield value1 / value2; } From 2786a0b4a755392457e668d38bb360b94254384b Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Fri, 10 Nov 2023 15:17:27 +0100 Subject: [PATCH 19/20] fix --- .../server/modifications/ByFormulaModification.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b6e4ca235..ddc54f980 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -175,7 +175,7 @@ private Double applyOperation(Operator operator, Double value1, Double value2) { case DIVISION -> { if (value2 == 0) { throw new NetworkModificationException(NetworkModificationException.Type.BY_FORMULA_MODIFICATION_ERROR, - "there is a division by zero in one formula"); + "there is a division by zero in a formula"); } else { yield value1 / value2; } From f093628d130c415da5b5b019e2ed433b1bf753f7 Mon Sep 17 00:00:00 2001 From: Seddik Yengui Date: Fri, 10 Nov 2023 16:36:56 +0100 Subject: [PATCH 20/20] replace var --- .../server/modifications/ByFormulaModification.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 ddc54f980..b64ce2f58 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ByFormulaModification.java @@ -59,7 +59,7 @@ public void check(Network network) throws NetworkModificationException { @Override public void apply(Network network, Reporter subReporter) { // collect all filters from all variations - var filters = modificationInfos.getFormulaInfosList().stream() + Map filters = modificationInfos.getFormulaInfosList().stream() .flatMap(v -> v.getFilters().stream()) .filter(distinctByKey(FilterInfos::getId)) .collect(Collectors.toMap(FilterInfos::getId, FilterInfos::getName)); @@ -88,7 +88,7 @@ private void applyFormulaOnFilterEquipments(Network network, List formulaReports, FormulaInfos formulaInfos, FilterInfos filterInfos) { - var filterEquipments = exportFilters.get(filterInfos.getId()); + FilterEquipments filterEquipments = exportFilters.get(filterInfos.getId()); formulaReports.add(Report.builder() .withKey("byFormulaModificationFormulaFilter_" + formulaReports.size()) @@ -104,7 +104,7 @@ private void applyFormulaOnFilterEquipments(Network network, .build()); if (!CollectionUtils.isEmpty(filterEquipments.getNotFoundEquipments())) { - var equipmentIds = String.join(", ", filterEquipments.getNotFoundEquipments()); + String equipmentIds = String.join(", ", filterEquipments.getNotFoundEquipments()); formulaReports.add(Report.builder() .withKey("filterEquipmentsNotFound_" + formulaReports.size()) .withDefaultMessage(String.format(" Equipment not found : %s",