diff --git a/src/main/java/org/gridsuite/modification/server/ModificationType.java b/src/main/java/org/gridsuite/modification/server/ModificationType.java index 86d1195f8..85a2b0ebe 100644 --- a/src/main/java/org/gridsuite/modification/server/ModificationType.java +++ b/src/main/java/org/gridsuite/modification/server/ModificationType.java @@ -43,7 +43,9 @@ public enum ModificationType { DELETE_VOLTAGE_LEVEL_ON_LINE(PreloadingStrategy.NONE), DELETE_ATTACHING_LINE(PreloadingStrategy.NONE), GENERATION_DISPATCH(PreloadingStrategy.COLLECTION), - VOLTAGE_INIT_MODIFICATION(PreloadingStrategy.COLLECTION); + VOLTAGE_INIT_MODIFICATION(PreloadingStrategy.COLLECTION), + VSC_CREATION(PreloadingStrategy.NONE), + CONVERTER_STATION_CREATION(PreloadingStrategy.NONE); 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 9563a4cf0..c6296dd0e 100644 --- a/src/main/java/org/gridsuite/modification/server/NetworkModificationException.java +++ b/src/main/java/org/gridsuite/modification/server/NetworkModificationException.java @@ -98,7 +98,11 @@ public enum Type { FILTERS_NOT_FOUND(HttpStatus.NOT_FOUND), GENERATION_DISPATCH_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), PRELOADING_STRATEGY_NOT_ALLOWED(HttpStatus.INTERNAL_SERVER_ERROR), - VOLTAGE_INIT_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR); + VOLTAGE_INIT_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + 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); public final HttpStatus status; private final String message; diff --git a/src/main/java/org/gridsuite/modification/server/dto/ConverterStationCreationInfos.java b/src/main/java/org/gridsuite/modification/server/dto/ConverterStationCreationInfos.java new file mode 100644 index 000000000..5276bee90 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/dto/ConverterStationCreationInfos.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.modification.server.dto; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.server.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.server.entities.equipment.creation.ConverterStationCreationEntity; + +import java.util.List; + +/** + * @author Seddik Yengui + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "Converter station creation") +@JsonTypeName("CONVERTER_STATION_CREATION") +@ModificationErrorTypeName("CREATE_CONVERTER_STATION_ERROR") +public class ConverterStationCreationInfos extends InjectionCreationInfos implements ReactiveLimitsHolderInfos { + @Schema(description = "Loss Factor") + private Float lossFactor; + + @Schema(description = "Reactive power") + private Double reactivePower; + + @Schema(description = "Voltage regulation") + private Boolean voltageRegulationOn; + + @Schema(description = "Voltage") + private Double voltage; + + @Schema(description = "Reactive capability curve") + private Boolean reactiveCapabilityCurve; + + @Schema(description = "Minimum reactive power") + private Double minimumReactivePower; + + @Schema(description = "Maximum reactive power") + private Double maximumReactivePower; + + @Schema(description = "Reactive capability curve points") + private List reactiveCapabilityCurvePoints; + + @Override + public ConverterStationCreationEntity toEntity() { + return new ConverterStationCreationEntity(this); + } +} 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 e55f616f6..f29291663 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/ModificationInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/ModificationInfos.java @@ -63,7 +63,9 @@ @JsonSubTypes.Type(value = DeleteVoltageLevelOnLineInfos.class), @JsonSubTypes.Type(value = DeleteAttachingLineInfos.class), @JsonSubTypes.Type(value = GenerationDispatchInfos.class), - @JsonSubTypes.Type(value = VoltageInitModificationInfos.class) + @JsonSubTypes.Type(value = VoltageInitModificationInfos.class), + @JsonSubTypes.Type(value = VscCreationInfos.class), + @JsonSubTypes.Type(value = ConverterStationCreationInfos.class) }) @SuperBuilder @NoArgsConstructor diff --git a/src/main/java/org/gridsuite/modification/server/dto/VscCreationInfos.java b/src/main/java/org/gridsuite/modification/server/dto/VscCreationInfos.java new file mode 100644 index 000000000..6f1ed2d92 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/dto/VscCreationInfos.java @@ -0,0 +1,88 @@ +/** + * 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.JsonTypeName; +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.commons.reporter.ReporterModel; +import com.powsybl.iidm.network.HvdcLine; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import org.gridsuite.modification.server.dto.annotation.ModificationErrorTypeName; +import org.gridsuite.modification.server.entities.equipment.creation.VscCreationEntity; +import org.gridsuite.modification.server.modifications.AbstractModification; +import org.gridsuite.modification.server.modifications.VscCreation; + +/** + * @author Seddik Yengui + */ + +@SuperBuilder +@NoArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +@Schema(description = "VSC creation") +@JsonTypeName("VSC_CREATION") +@ModificationErrorTypeName("CREATE_VSC_ERROR") +public class VscCreationInfos extends EquipmentCreationInfos { + @Schema(description = "DC nominal voltage") + private Double dcNominalVoltage; + + @Schema(description = "DC resistance") + private Double dcResistance; + + @Schema(description = "Maximum active power ") + private Double maximumActivePower; + + @Schema(description = "Operator active power limit (Side1->Side2)") + private Float operatorActivePowerLimitFromSide1ToSide2; + + @Schema(description = "Operator active power limit (Side2->Side1)") + private Float operatorActivePowerLimitFromSide2ToSide1; + + @Schema(description = "Converters mode") + private HvdcLine.ConvertersMode convertersMode; + + @Schema(description = "Active power") + private Double activePower; + + @Schema(description = "Angle droop active power control ") + private Boolean angleDroopActivePowerControl; + + @Schema(description = "p0") + private Float p0; + + @Schema(description = "droop") + private Float droop; + + @Schema(description = "Converter station 1") + private ConverterStationCreationInfos converterStation1; + + @Schema(description = "Converter station 2") + private ConverterStationCreationInfos converterStation2; + + @Override + public VscCreationEntity toEntity() { + return new VscCreationEntity(this); + } + + @Override + public AbstractModification toModification() { + return new VscCreation(this); + } + + @Override + public Reporter createSubReporter(ReporterModel reporter) { + return reporter.createSubReporter(getType().name(), "Vsc creation ${vscId}", "vscId", this.getEquipmentId()); + } +} diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/creation/ConverterStationCreationEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/creation/ConverterStationCreationEntity.java new file mode 100644 index 000000000..7914d5cd8 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/creation/ConverterStationCreationEntity.java @@ -0,0 +1,126 @@ +/** + * 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.creation; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.gridsuite.modification.server.dto.ConverterStationCreationInfos; +import org.gridsuite.modification.server.dto.ReactiveCapabilityCurveCreationInfos; + +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author Seddik Yengui + */ + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Entity +@Table(name = "converterStationCreation") +public class ConverterStationCreationEntity extends InjectionCreationEntity { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id") + private UUID id; + + @Column + private Float lossFactor; + + @Column + private Double minimumReactivePower; + + @Column + private Double maximumReactivePower; + + @Column + private Double reactivePower; + + @Column + private Boolean voltageRegulationOn; + + @Column + private Double voltage; + + @ElementCollection + @CollectionTable(name = "converter_station_creation_rcc_points") + private List reactiveCapabilityCurvePoints; + + @Column + private Boolean reactiveCapabilityCurve; + + public ConverterStationCreationEntity(ConverterStationCreationInfos converterStationCreationInfos) { + super(converterStationCreationInfos); + assignAttributes(converterStationCreationInfos); + } + + private void assignAttributes(ConverterStationCreationInfos converterStationCreationInfos) { + this.lossFactor = converterStationCreationInfos.getLossFactor(); + this.minimumReactivePower = converterStationCreationInfos.getMinimumReactivePower(); + this.maximumReactivePower = converterStationCreationInfos.getMaximumReactivePower(); + this.reactivePower = converterStationCreationInfos.getReactivePower(); + this.voltageRegulationOn = converterStationCreationInfos.getVoltageRegulationOn(); + this.voltage = converterStationCreationInfos.getVoltage(); + this.reactiveCapabilityCurvePoints = toEmbeddablePoints(converterStationCreationInfos.getReactiveCapabilityCurvePoints()); + this.reactiveCapabilityCurve = converterStationCreationInfos.getReactiveCapabilityCurve(); + } + + public ConverterStationCreationInfos toConverterStationInfos() { + return ConverterStationCreationInfos.builder() + .equipmentId(getEquipmentId()) + .equipmentName(getEquipmentName()) + .lossFactor(getLossFactor()) + .minimumReactivePower(getMinimumReactivePower()) + .maximumReactivePower(getMaximumReactivePower()) + .reactivePower(getReactivePower()) + .voltageRegulationOn(getVoltageRegulationOn()) + .voltage(getVoltage()) + .reactiveCapabilityCurvePoints(toReactiveCapabilityCurveInfos(getReactiveCapabilityCurvePoints())) + .voltageLevelId(getVoltageLevelId()) + .busOrBusbarSectionId(getBusOrBusbarSectionId()) + .connectionName(getConnectionName()) + .connectionPosition(getConnectionPosition()) + .connectionDirection(getConnectionDirection()) + .reactiveCapabilityCurve(getReactiveCapabilityCurve()) + .build(); + } + + private static List toEmbeddablePoints( + List points) { + return points == null ? null : points.stream() + .map(point -> new ReactiveCapabilityCurveCreationEmbeddable(point.getQminP(), + point.getQmaxP(), + point.getP())) + .collect(Collectors.toList()); + } + + private static List toReactiveCapabilityCurveInfos(List pointsEmbeddable) { + if (pointsEmbeddable == null) { + return null; + } + + return pointsEmbeddable.stream() + .map(pointEmbeddable -> ReactiveCapabilityCurveCreationInfos.builder() + .p(pointEmbeddable.getP()) + .qmaxP(pointEmbeddable.getQmaxP()) + .qminP(pointEmbeddable.getQminP()) + .build()) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/creation/VscCreationEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/creation/VscCreationEntity.java new file mode 100644 index 000000000..49c7acad8 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/creation/VscCreationEntity.java @@ -0,0 +1,137 @@ +/** + * 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.creation; + +import com.powsybl.iidm.network.HvdcLine; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import org.gridsuite.modification.server.dto.ConverterStationCreationInfos; +import org.gridsuite.modification.server.dto.ModificationInfos; +import org.gridsuite.modification.server.dto.VscCreationInfos; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.ForeignKey; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +/** + * @author Seddik Yengui + */ + +@NoArgsConstructor +@Getter +@Entity +@Table(name = "vscCreation") +public class VscCreationEntity extends EquipmentCreationEntity { + @Column + private Double dcNominalVoltage; + + @Column + private Double dcResistance; + + @Column + private Double maximumActivePower; + + @Column + private Float operatorActivePowerLimitSide1; + + @Column + private Float operatorActivePowerLimitSide2; + + @Column + private HvdcLine.ConvertersMode convertersMode; + + @Column + private Double activePower; + + @Column + private Boolean angleDroopActivePowerControl; + + @Column + private Float p0; + + @Column + private Float droop; + + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @JoinColumn( + name = "converter_station_1_id", + referencedColumnName = "id", + foreignKey = @ForeignKey( + name = "converter_station_1_id_fk" + )) + private ConverterStationCreationEntity converterStation1; + + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @JoinColumn( + name = "converter_station_2_id", + referencedColumnName = "id", + foreignKey = @ForeignKey( + name = "converter_station_2_id_fk" + )) + private ConverterStationCreationEntity converterStation2; + + public VscCreationEntity(@NonNull VscCreationInfos vscCreationInfos) { + super(vscCreationInfos); + assignAttributes(vscCreationInfos); + } + + @Override + public void update(@NonNull ModificationInfos modificationInfos) { + super.update(modificationInfos); + assignAttributes((VscCreationInfos) modificationInfos); + } + + private void assignAttributes(VscCreationInfos vscCreationInfos) { + this.activePower = vscCreationInfos.getActivePower(); + this.angleDroopActivePowerControl = vscCreationInfos.getAngleDroopActivePowerControl(); + this.droop = vscCreationInfos.getDroop(); + this.convertersMode = vscCreationInfos.getConvertersMode(); + this.dcNominalVoltage = vscCreationInfos.getDcNominalVoltage(); + this.dcResistance = vscCreationInfos.getDcResistance(); + this.operatorActivePowerLimitSide1 = vscCreationInfos.getOperatorActivePowerLimitFromSide1ToSide2(); + this.operatorActivePowerLimitSide2 = vscCreationInfos.getOperatorActivePowerLimitFromSide2ToSide1(); + this.maximumActivePower = vscCreationInfos.getMaximumActivePower(); + this.p0 = vscCreationInfos.getP0(); + this.converterStation1 = vscCreationInfos.getConverterStation1().toEntity(); + this.converterStation2 = vscCreationInfos.getConverterStation2().toEntity(); + } + + @Override + public VscCreationInfos toModificationInfos() { + return toVscCreationInfosBuilder().build(); + } + + private VscCreationInfos.VscCreationInfosBuilder toVscCreationInfosBuilder() { + ConverterStationCreationInfos converterStationCreationInfos1 = getConverterStation1() == null ? null : getConverterStation1().toConverterStationInfos(); + ConverterStationCreationInfos converterStationCreationInfos2 = getConverterStation2() == null ? null : getConverterStation2().toConverterStationInfos(); + + return VscCreationInfos.builder() + .uuid(getId()) + .date(getDate()) + .equipmentId(getEquipmentId()) + .equipmentName(getEquipmentName()) + .activePower(getActivePower()) + .angleDroopActivePowerControl(getAngleDroopActivePowerControl()) + .droop(getDroop()) + .convertersMode(getConvertersMode()) + .dcNominalVoltage(getDcNominalVoltage()) + .dcResistance(getDcResistance()) + .operatorActivePowerLimitFromSide1ToSide2(getOperatorActivePowerLimitSide1()) + .operatorActivePowerLimitFromSide2ToSide1(getOperatorActivePowerLimitSide2()) + .maximumActivePower(getMaximumActivePower()) + .p0(getP0()) + .converterStation1(converterStationCreationInfos1) + .converterStation2(converterStationCreationInfos2); + } +} diff --git a/src/main/java/org/gridsuite/modification/server/modifications/GeneratorCreation.java b/src/main/java/org/gridsuite/modification/server/modifications/GeneratorCreation.java index 00ad6b261..afc0bc849 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/GeneratorCreation.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/GeneratorCreation.java @@ -190,8 +190,9 @@ private Reporter reportGeneratorSetPoints(GeneratorCreationInfos generatorCreati private void createGeneratorVoltageRegulation(GeneratorCreationInfos generatorCreationInfos, Generator generator, VoltageLevel voltageLevel, Reporter subReporter) { List voltageReports = new ArrayList<>(); + voltageReports.add(ModificationUtils.getInstance() + .createEnabledDisabledReport("VoltageRegulationOn", modificationInfos.isVoltageRegulationOn())); voltageReports.add(ModificationUtils.getInstance().buildCreationReport(generatorCreationInfos.getVoltageSetpoint(), "Voltage")); - voltageReports.add(ModificationUtils.getInstance().buildCreationReport(generatorCreationInfos.isVoltageRegulationOn(), "VoltageRegulationOn")); if (generatorCreationInfos.getRegulatingTerminalVlId() != null && generatorCreationInfos.getRegulatingTerminalId() != null && generatorCreationInfos.getRegulatingTerminalType() != null) { Terminal terminal = ModificationUtils.getInstance().getTerminalFromIdentifiable(voltageLevel.getNetwork(), 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 4cb5e7959..d89c8fcf9 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/ModificationUtils.java @@ -97,6 +97,14 @@ Generator getGenerator(Network network, String generatorId) { return generator; } + VscConverterStation getVscConverterStation(Network network, String converterStationId) { + VscConverterStation vscConverterStation = network.getVscConverterStation(converterStationId); + if (vscConverterStation == null) { + throw new NetworkModificationException(VSC_CONVERTER_STATION_NOT_FOUND, "Vsc converter station " + converterStationId + " does not exist in network"); + } + return vscConverterStation; + } + public void controlConnectivity(Network network, String voltageLevelId, String busOrBusbarSectionId, Integer connectionPosition) { VoltageLevel voltageLevel = getVoltageLevel(network, voltageLevelId); if (voltageLevel.getTopologyKind() == TopologyKind.NODE_BREAKER) { @@ -430,6 +438,13 @@ public Report applyElementaryModificationsAndReturnReport(Consumer setter return null; } + public Report createEnabledDisabledReport(String key, boolean enabled) { + return Report.builder().withKey(key) + .withDefaultMessage(enabled ? " Enabled" : " Disables") + .withSeverity(TypedValue.INFO_SEVERITY) + .build(); + } + public Reporter reportModifications(Reporter subReporter, List reports, String subReporterKey, String subReporterDefaultMessage) { List validReports = reports.stream().filter(Objects::nonNull).toList(); diff --git a/src/main/java/org/gridsuite/modification/server/modifications/VscCreation.java b/src/main/java/org/gridsuite/modification/server/modifications/VscCreation.java new file mode 100644 index 000000000..41f874dc5 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/modifications/VscCreation.java @@ -0,0 +1,316 @@ +/** + * 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.commons.reporter.Report; +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.commons.reporter.TypedValue; +import com.powsybl.iidm.modification.topology.CreateFeederBay; +import com.powsybl.iidm.modification.topology.CreateFeederBayBuilder; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TopologyKind; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.iidm.network.VscConverterStation; +import com.powsybl.iidm.network.VscConverterStationAdder; +import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControlAdder; +import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRangeAdder; +import org.gridsuite.modification.server.NetworkModificationException; +import org.gridsuite.modification.server.dto.ConverterStationCreationInfos; +import org.gridsuite.modification.server.dto.VscCreationInfos; + +import java.util.ArrayList; +import java.util.List; + +import static org.gridsuite.modification.server.NetworkModificationException.Type.CREATE_VSC_ERROR; +import static org.gridsuite.modification.server.NetworkModificationException.Type.HVDC_LINE_ALREADY_EXISTS; + +/** + * @author Seddik Yengui + */ + +public class VscCreation extends AbstractModification { + public static final String CHARACTERISTICS = "Characteristics"; + public static final String SETPOINTS = "Setpoints"; + private final VscCreationInfos modificationInfos; + + public VscCreation(VscCreationInfos modificationInfos) { + this.modificationInfos = modificationInfos; + } + + @Override + public void check(Network network) throws NetworkModificationException { + if (network.getHvdcLine(modificationInfos.getEquipmentId()) != null) { + throw new NetworkModificationException(HVDC_LINE_ALREADY_EXISTS, modificationInfos.getEquipmentId()); + } + + checkConverterStation(network, modificationInfos.getConverterStation1()); + checkConverterStation(network, modificationInfos.getConverterStation2()); + } + + private void checkConverterStation(Network network, + ConverterStationCreationInfos converterStation) { + if (converterStation == null) { + throw new NetworkModificationException(CREATE_VSC_ERROR, modificationInfos.getEquipmentId() + "Missing required converter station"); + } + // check connectivity + ModificationUtils.getInstance().controlConnectivity(network, + converterStation.getVoltageLevelId(), + converterStation.getBusOrBusbarSectionId(), + converterStation.getConnectionPosition()); + + // check reactive limits + ModificationUtils.getInstance().checkReactiveLimitsCreation(converterStation, + CREATE_VSC_ERROR, + modificationInfos.getEquipmentId(), + "Vsc"); + } + + @Override + public void apply(Network network, Reporter subReporter) { + VscConverterStation converterStation1 = createConverterStation(network, modificationInfos.getConverterStation1(), subReporter, "Converter station 1"); + + VscConverterStation converterStation2 = createConverterStation(network, modificationInfos.getConverterStation2(), subReporter, "Converter station 2"); + + HvdcLine hvdcLine = network.newHvdcLine() + .setId(modificationInfos.getEquipmentId()) + .setName(modificationInfos.getEquipmentName()) + .setNominalV(modificationInfos.getDcNominalVoltage()) + .setR(modificationInfos.getDcResistance()) + .setMaxP(modificationInfos.getMaximumActivePower()) + .setActivePowerSetpoint(modificationInfos.getActivePower()) + .setConvertersMode(modificationInfos.getConvertersMode()) + .setConverterStationId1(converterStation1 != null ? converterStation1.getId() : null) + .setConverterStationId2(converterStation2 != null ? converterStation2.getId() : null) + .add(); + + if (modificationInfos.getOperatorActivePowerLimitFromSide1ToSide2() != null || + modificationInfos.getOperatorActivePowerLimitFromSide2ToSide1() != null) { + hvdcLine.newExtension(HvdcOperatorActivePowerRangeAdder.class) + .withOprFromCS1toCS2(modificationInfos.getOperatorActivePowerLimitFromSide1ToSide2()) + .withOprFromCS2toCS1(modificationInfos.getOperatorActivePowerLimitFromSide2ToSide1()) + .add(); + } + + if (modificationInfos.getDroop() != null || + modificationInfos.getP0() != null) { + var activePowerControlExtension = hvdcLine.newExtension(HvdcAngleDroopActivePowerControlAdder.class) + .withEnabled(modificationInfos.getAngleDroopActivePowerControl()); + if (modificationInfos.getP0() != null) { + activePowerControlExtension.withP0(modificationInfos.getP0()); + } + + if (modificationInfos.getDroop() != null) { + activePowerControlExtension.withDroop(modificationInfos.getDroop()); + } + + activePowerControlExtension.add(); + } + reportHvdcLineInfos(subReporter); + + subReporter.report(Report.builder() + .withKey("vscCreated") + .withDefaultMessage("New vsc with id=${id} created") + .withValue("id", modificationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + + if (modificationInfos.getEquipmentName() != null) { + ModificationUtils.getInstance() + .reportElementaryCreation(subReporter, modificationInfos.getEquipmentName(), "Name"); + } + } + + private void reportHvdcLineInfos(Reporter subReporter) { + List characteristicsReports = new ArrayList<>(); + Reporter characteristicReport = subReporter.createSubReporter("vscCharacteristics", CHARACTERISTICS); + characteristicsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getDcNominalVoltage(), "DC nominal voltage")); + characteristicsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getDcResistance(), "DC resistance")); + characteristicsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getMaximumActivePower(), "Pmax")); + ModificationUtils.getInstance().reportModifications(characteristicReport, characteristicsReports, "vscCharacteristics", CHARACTERISTICS); + + List limitsReports = new ArrayList<>(); + Reporter limitsReport = subReporter.createSubReporter("vscLimits", "Limits"); + limitsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getOperatorActivePowerLimitFromSide1ToSide2(), "Operator active power limit (Side1 -> Side 2)")); + limitsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getOperatorActivePowerLimitFromSide2ToSide1(), "Operator active power limit (Side2 -> Side 1)")); + ModificationUtils.getInstance().reportModifications(limitsReport, limitsReports, "vscLimits", "Limits"); + + List setPointsReports = new ArrayList<>(); + Reporter setPointsReporter = subReporter.createSubReporter("vscSetPoints", SETPOINTS); + setPointsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getConvertersMode(), "Converters mode")); + setPointsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getActivePower(), "Active power")); + setPointsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getMaximumActivePower(), "Pmax")); + ModificationUtils.getInstance().reportModifications(setPointsReporter, setPointsReports, "vscSetPoints", SETPOINTS); + + List angleDroopActivePowerControlReports = new ArrayList<>(); + angleDroopActivePowerControlReports.add(ModificationUtils.getInstance() + .createEnabledDisabledReport("angleDroopActivePowerControl", modificationInfos.getAngleDroopActivePowerControl())); + + if (modificationInfos.getP0() != null) { + angleDroopActivePowerControlReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getP0(), "P0")); + } + + if (modificationInfos.getDroop() != null) { + angleDroopActivePowerControlReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getDroop(), "Droop")); + } + + ModificationUtils.getInstance().reportModifications(setPointsReporter, angleDroopActivePowerControlReports, "vscAngleDroop", "Angle droop active power control"); + } + + private VscConverterStation createConverterStation(Network network, + ConverterStationCreationInfos converterStationCreationInfos, + Reporter subReporter, + String logFieldName) { + Reporter converterStationReporter = subReporter.createSubReporter("converterStationCreated" + logFieldName, logFieldName); + VoltageLevel voltageLevel = ModificationUtils.getInstance().getVoltageLevel(network, converterStationCreationInfos.getVoltageLevelId()); + VscConverterStation vscConverterStation = voltageLevel.getTopologyKind() == TopologyKind.NODE_BREAKER ? + createConverterStationInNodeBreaker(network, voltageLevel, converterStationCreationInfos, converterStationReporter) : + createConverterStationInBusBreaker(voltageLevel, converterStationCreationInfos, converterStationReporter); + + converterStationReporter.report(Report.builder() + .withKey("converterStationCreated" + logFieldName) + .withDefaultMessage("New converter station with id=${id} created") + .withValue("id", converterStationCreationInfos.getEquipmentId()) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + + return vscConverterStation; + } + + private VscConverterStation createConverterStationInNodeBreaker(Network network, + VoltageLevel voltageLevel, + ConverterStationCreationInfos converterStationCreationInfos, + Reporter subReporter) { + VscConverterStationAdder converterStationAdder = voltageLevel.newVscConverterStation() + .setId(converterStationCreationInfos.getEquipmentId()) + .setName(converterStationCreationInfos.getEquipmentName()) + .setVoltageRegulatorOn(converterStationCreationInfos.getVoltageRegulationOn()); + + if (converterStationCreationInfos.getReactivePower() != null) { + converterStationAdder.setReactivePowerSetpoint(converterStationCreationInfos.getReactivePower()); + } + + if (converterStationCreationInfos.getLossFactor() != null) { + converterStationAdder.setLossFactor(converterStationCreationInfos.getLossFactor()); + } + + if (converterStationCreationInfos.getVoltage() != null) { + converterStationAdder.setVoltageSetpoint(converterStationCreationInfos.getVoltage()); + } + int position = ModificationUtils.getInstance().getPosition(converterStationCreationInfos.getConnectionPosition(), + converterStationCreationInfos.getBusOrBusbarSectionId(), + network, + voltageLevel); + + CreateFeederBay algo = new CreateFeederBayBuilder() + .withBusOrBusbarSectionId(converterStationCreationInfos.getBusOrBusbarSectionId()) + .withInjectionDirection(converterStationCreationInfos.getConnectionDirection()) + .withInjectionFeederName(converterStationCreationInfos.getConnectionName() != null + ? converterStationCreationInfos.getConnectionName() + : converterStationCreationInfos.getEquipmentId()) + .withInjectionPositionOrder(position) + .withInjectionAdder(converterStationAdder) + .build(); + + algo.apply(network, true, subReporter); + VscConverterStation vscConverterStation = ModificationUtils.getInstance() + .getVscConverterStation(network, converterStationCreationInfos.getEquipmentId()); + + addExtensionsAndReports(vscConverterStation, + converterStationCreationInfos, + subReporter); + + return vscConverterStation; + + } + + private VscConverterStation createConverterStationInBusBreaker(VoltageLevel voltageLevel, + ConverterStationCreationInfos converterStationCreationInfos, + Reporter subReporter) { + Bus bus = ModificationUtils.getInstance().getBusBreakerBus(voltageLevel, converterStationCreationInfos.getBusOrBusbarSectionId()); + VscConverterStation vscConverterStation = voltageLevel.newVscConverterStation() + .setId(converterStationCreationInfos.getEquipmentId()) + .setName(converterStationCreationInfos.getEquipmentName()) + .setVoltageRegulatorOn(converterStationCreationInfos.getVoltageRegulationOn()) + .setReactivePowerSetpoint(converterStationCreationInfos.getReactivePower()) + .setBus(bus.getId()) + .setLossFactor(converterStationCreationInfos.getLossFactor()) + .setVoltageSetpoint(converterStationCreationInfos.getVoltage()) + .add(); + + addExtensionsAndReports(vscConverterStation, converterStationCreationInfos, subReporter); + + return vscConverterStation; + } + + private void addExtensionsAndReports(VscConverterStation vscConverterStation, + ConverterStationCreationInfos converterStationCreationInfos, + Reporter subReporter) { + reportConnectivity(converterStationCreationInfos, subReporter); + + ModificationUtils.getInstance().reportModifications(subReporter, + List.of(ModificationUtils.getInstance().buildCreationReport(converterStationCreationInfos.getLossFactor(), "Loss Factor")), + "converterStationCharacteristics", + CHARACTERISTICS); + + ModificationUtils.getInstance().createReactiveLimits(converterStationCreationInfos, vscConverterStation, subReporter); + + reportConverterStationSetPoints(converterStationCreationInfos, subReporter); + } + + private void reportConverterStationSetPoints(ConverterStationCreationInfos converterStationCreationInfos, Reporter subReporter) { + Reporter setPointReporter = subReporter.createSubReporter("converterStationSetPoint", SETPOINTS); + setPointReporter.report(Report.builder() + .withKey(SETPOINTS) + .withDefaultMessage(SETPOINTS) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + + if (converterStationCreationInfos.getReactivePower() != null) { + ModificationUtils.getInstance().reportElementaryCreation(setPointReporter, + converterStationCreationInfos.getReactivePower(), + "Reactive power"); + } + + List setPointsVoltageReports = new ArrayList<>(); + setPointsVoltageReports.add(ModificationUtils.getInstance().createEnabledDisabledReport("voltageRegulationOn", + converterStationCreationInfos.getVoltageRegulationOn())); + if (converterStationCreationInfos.getVoltage() != null) { + setPointsVoltageReports.add(ModificationUtils.getInstance().buildCreationReport(converterStationCreationInfos.getReactivePower(), "Voltage")); + } + + ModificationUtils.getInstance().reportModifications(setPointReporter, + setPointsVoltageReports, + "converterStationSetPointsVoltageRegulation", + "Voltage regulation"); + } + + private void reportConnectivity(ConverterStationCreationInfos converterStationCreationInfos, Reporter subReporter) { + if (converterStationCreationInfos.getConnectionName() == null && + converterStationCreationInfos.getConnectionDirection() == null && + converterStationCreationInfos.getConnectionPosition() == null) { + return; + } + + List connectivityReports = new ArrayList<>(); + if (converterStationCreationInfos.getConnectionName() != null) { + connectivityReports.add(ModificationUtils.getInstance() + .buildCreationReport(converterStationCreationInfos.getConnectionName(), "Connection name")); + } + if (converterStationCreationInfos.getConnectionDirection() != null) { + connectivityReports.add(ModificationUtils.getInstance() + .buildCreationReport(converterStationCreationInfos.getConnectionDirection(), "Connection direction")); + } + if (converterStationCreationInfos.getConnectionPosition() != null) { + connectivityReports.add(ModificationUtils.getInstance() + .buildCreationReport(converterStationCreationInfos.getConnectionPosition(), "Connection position")); + } + ModificationUtils.getInstance().reportModifications(subReporter, connectivityReports, "ConnectivityCreated", "Connectivity"); + } +} diff --git a/src/main/resources/db/changelog/changesets/changelog_20230917T220327Z.xml b/src/main/resources/db/changelog/changesets/changelog_20230917T220327Z.xml new file mode 100644 index 000000000..fb641d107 --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20230917T220327Z.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index c7c8a0200..070945dfe 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -194,4 +194,7 @@ databaseChangeLog: relativeToChangelogFile: true - include: file: changesets/changelog_20230907T121742Z.xml - relativeToChangelogFile: true \ No newline at end of file + relativeToChangelogFile: true + - include: + file: changesets/changelog_20230917T220327Z.xml + relativeToChangelogFile: true diff --git a/src/test/java/org/gridsuite/modification/server/modifications/VscCreationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/VscCreationTest.java new file mode 100644 index 000000000..9324bf8fa --- /dev/null +++ b/src/test/java/org/gridsuite/modification/server/modifications/VscCreationTest.java @@ -0,0 +1,285 @@ +/** + * 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.HvdcLine; +import com.powsybl.iidm.network.MinMaxReactiveLimits; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ReactiveCapabilityCurve; +import com.powsybl.iidm.network.ReactiveLimitsKind; +import com.powsybl.iidm.network.VscConverterStation; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControl; +import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRange; +import org.gridsuite.modification.server.NetworkModificationException; +import org.gridsuite.modification.server.dto.ConverterStationCreationInfos; +import org.gridsuite.modification.server.dto.ModificationInfos; +import org.gridsuite.modification.server.dto.ReactiveCapabilityCurveCreationInfos; +import org.gridsuite.modification.server.dto.VscCreationInfos; +import org.gridsuite.modification.server.utils.NetworkCreation; +import org.junit.Test; +import org.springframework.http.MediaType; + +import java.util.List; +import java.util.UUID; + +import static org.gridsuite.modification.server.NetworkModificationException.Type.CREATE_VSC_ERROR; +import static org.gridsuite.modification.server.NetworkModificationException.Type.HVDC_LINE_ALREADY_EXISTS; +import static org.gridsuite.modification.server.NetworkModificationException.Type.VOLTAGE_LEVEL_NOT_FOUND; +import static org.gridsuite.modification.server.utils.TestUtils.assertLogMessage; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Seddik Yengui + */ + +public class VscCreationTest extends AbstractNetworkModificationTest { + @Override + protected Network createNetwork(UUID networkUuid) { + return NetworkCreation.create(networkUuid, true); + } + + @Override + protected ModificationInfos buildModification() { + return VscCreationInfos.builder() + .equipmentId("vsc1") + .equipmentName("vsc1Name") + .dcNominalVoltage(39.) + .dcResistance(4.) + .maximumActivePower(56.) + .p0(5F) + .operatorActivePowerLimitFromSide2ToSide1(5.6F) + .convertersMode(HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER) + .activePower(5.) + .operatorActivePowerLimitFromSide1ToSide2(6.0F) + .operatorActivePowerLimitFromSide2ToSide1(8F) + .droop(1F) + .angleDroopActivePowerControl(false) + .converterStation1(buildConverterStationWithReactiveCapabilityCurve()) + .converterStation2(buildConverterStationWithMinMaxReactiveLimits()) + .build(); + } + + private ConverterStationCreationInfos buildConverterStationWithMinMaxReactiveLimits() { + return ConverterStationCreationInfos.builder() + .equipmentId("stationId2") + .equipmentName("station2") + .voltageRegulationOn(false) + .reactivePower(23.) + .reactiveCapabilityCurve(false) + .maximumReactivePower(66.) + .lossFactor(4F) + .minimumReactivePower(55.) + .voltage(34.) + .voltageLevelId("v2") + .busOrBusbarSectionId("1.1") + .connectionName("top") + .connectionDirection(ConnectablePosition.Direction.TOP) + .reactiveCapabilityCurvePoints(List.of()) + .reactiveCapabilityCurve(false) + .build(); + } + + private ConverterStationCreationInfos buildConverterStationWithReactiveCapabilityCurve() { + var point1 = ReactiveCapabilityCurveCreationInfos.builder() + .p(0.4) + .qmaxP(3.) + .qminP(0.) + .build(); + var point2 = ReactiveCapabilityCurveCreationInfos.builder() + .p(0.6) + .qmaxP(2.) + .qminP(1.1) + .build(); + + return ConverterStationCreationInfos.builder() + .equipmentId("stationId1") + .equipmentName("station1") + .voltageRegulationOn(true) + .voltage(66.) + .reactivePower(44.) + .lossFactor(40F) + .reactiveCapabilityCurve(true) + .reactiveCapabilityCurvePoints(List.of(point1, point2)) + .voltageLevelId("v1") + .busOrBusbarSectionId("1.1") + .connectionName("top") + .connectionDirection(ConnectablePosition.Direction.TOP) + .build(); + } + + @Override + protected ModificationInfos buildModificationUpdate() { + return VscCreationInfos.builder() + .equipmentId("vsc1") + .equipmentName("vsc2Name") + .dcNominalVoltage(53.) + .dcResistance(2.) + .maximumActivePower(77.) + .p0(8.3F) + .operatorActivePowerLimitFromSide2ToSide1(5.2F) + .convertersMode(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER) + .activePower(7.) + .operatorActivePowerLimitFromSide1ToSide2(6.1F) + .operatorActivePowerLimitFromSide2ToSide1(8.3F) + .angleDroopActivePowerControl(true) + .droop(2.1F) + .converterStation1(buildConverterStationWithMinMaxReactiveLimits()) + .converterStation2(buildConverterStationWithReactiveCapabilityCurve()) + .build(); + } + + @Override + protected void assertAfterNetworkModificationCreation() { + assertNotNull(getNetwork().getHvdcLine("vsc1")); + + assertEquals(1, getNetwork().getVoltageLevel("v1").getVscConverterStationStream() + .filter(converterStation -> converterStation.getId().equals("stationId1")).count()); + + assertEquals(1, getNetwork().getVoltageLevel("v2").getVscConverterStationStream() + .filter(converterStation -> converterStation.getId().equals("stationId2")).count()); + + HvdcLine hvdcLine = getNetwork().getHvdcLine("vsc1"); + assertNotNull(hvdcLine); + assertEquals(HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER, hvdcLine.getConvertersMode()); + assertEquals(39, hvdcLine.getNominalV(), 0); + assertEquals(4, hvdcLine.getR(), 0); + assertEquals(5, hvdcLine.getActivePowerSetpoint(), 0); + assertEquals(56, hvdcLine.getMaxP(), 0); + + HvdcOperatorActivePowerRange hvdcOperatorActivePowerRange = hvdcLine.getExtension(HvdcOperatorActivePowerRange.class); + assertEquals(6, hvdcOperatorActivePowerRange.getOprFromCS1toCS2(), 0); + assertEquals(8, hvdcOperatorActivePowerRange.getOprFromCS2toCS1(), 0); + + HvdcAngleDroopActivePowerControl activePowerControl = hvdcLine.getExtension(HvdcAngleDroopActivePowerControl.class); + assertEquals(1, activePowerControl.getDroop(), 0); + assertEquals(5, activePowerControl.getP0(), 0); + + VscConverterStation vscConverterStation1 = (VscConverterStation) hvdcLine.getConverterStation1(); + assertNotNull(vscConverterStation1); + assertEquals(44, vscConverterStation1.getReactivePowerSetpoint(), 0); + assertEquals(40, vscConverterStation1.getLossFactor(), 0); + assertEquals(ReactiveLimitsKind.CURVE, vscConverterStation1.getReactiveLimits().getKind()); + ReactiveCapabilityCurve reactiveLimits1 = vscConverterStation1.getReactiveLimits(ReactiveCapabilityCurve.class); + assertEquals(2, reactiveLimits1.getPointCount()); + assertEquals(0.6, reactiveLimits1.getMaxP(), 0); + assertEquals(0.4, reactiveLimits1.getMinP(), 0); + assertEquals(66, vscConverterStation1.getVoltageSetpoint(), 0); + assertEquals("v1", vscConverterStation1.getTerminal().getVoltageLevel().getId()); + + VscConverterStation vscConverterStation2 = (VscConverterStation) hvdcLine.getConverterStation2(); + assertNotNull(vscConverterStation2); + assertEquals(23, vscConverterStation2.getReactivePowerSetpoint(), 0); + assertEquals(4, vscConverterStation2.getLossFactor(), 0); + assertEquals(ReactiveLimitsKind.MIN_MAX, vscConverterStation2.getReactiveLimits().getKind()); + MinMaxReactiveLimits reactiveLimits2 = vscConverterStation2.getReactiveLimits(MinMaxReactiveLimits.class); + assertEquals(66, reactiveLimits2.getMaxQ(), 0); + assertEquals(55, reactiveLimits2.getMinQ(), 0); + assertEquals(34, vscConverterStation2.getVoltageSetpoint(), 0); + assertEquals("v2", vscConverterStation2.getTerminal().getVoltageLevel().getId()); + } + + @Override + protected void assertAfterNetworkModificationDeletion() { + assertNull(getNetwork().getHvdcLine("vsc1")); + + assertEquals(0, getNetwork().getVoltageLevel("v1").getVscConverterStationStream() + .filter(converterStation -> converterStation.getId().equals("stationId1")).count()); + + assertEquals(0, getNetwork().getVoltageLevel("v2").getVscConverterStationStream() + .filter(converterStation -> converterStation.getId().equals("stationId2")).count()); + } + + @Test + public void testCreateWithErrors() throws Exception { + VscCreationInfos vscCreationInfos = (VscCreationInfos) buildModification(); + vscCreationInfos.setEquipmentId(""); + String vscCreationInfosJson = mapper.writeValueAsString(vscCreationInfos); + mockMvc.perform(post(getNetworkModificationUri()).content(vscCreationInfosJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + assertLogMessage("Invalid id ''", vscCreationInfos.getErrorType().name(), reportService); + + // not found voltage level + vscCreationInfos.setEquipmentId("vscId"); + ConverterStationCreationInfos converterStationCreationInfos = buildConverterStationWithMinMaxReactiveLimits(); + converterStationCreationInfos.setVoltageLevelId("notFoundVoltageLevelId"); + vscCreationInfos.setConverterStation2(converterStationCreationInfos); + vscCreationInfosJson = mapper.writeValueAsString(vscCreationInfos); + mockMvc.perform(post(getNetworkModificationUri()).content(vscCreationInfosJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + assertLogMessage(new NetworkModificationException(VOLTAGE_LEVEL_NOT_FOUND, "notFoundVoltageLevelId").getMessage(), + vscCreationInfos.getErrorType().name(), reportService); + + // invalid min max reactive limit + vscCreationInfos = (VscCreationInfos) buildModification(); + converterStationCreationInfos = buildConverterStationWithMinMaxReactiveLimits(); + converterStationCreationInfos.setConnectionPosition(35); + converterStationCreationInfos.setReactiveCapabilityCurve(false); + converterStationCreationInfos.setMinimumReactivePower(Double.NaN); + vscCreationInfos.setConverterStation1(converterStationCreationInfos); + + vscCreationInfosJson = mapper.writeValueAsString(vscCreationInfos); + mockMvc.perform(post(getNetworkModificationUri()).content(vscCreationInfosJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + assertLogMessage(new NetworkModificationException(CREATE_VSC_ERROR, "Vsc 'vsc1' : minimum reactive power is not set").getMessage(), + vscCreationInfos.getErrorType().name(), reportService); + + vscCreationInfos = (VscCreationInfos) buildModification(); + converterStationCreationInfos = buildConverterStationWithMinMaxReactiveLimits(); + converterStationCreationInfos.setConnectionPosition(66); + converterStationCreationInfos.setReactiveCapabilityCurve(false); + converterStationCreationInfos.setMaximumReactivePower(Double.NaN); + vscCreationInfos.setConverterStation1(converterStationCreationInfos); + + vscCreationInfosJson = mapper.writeValueAsString(vscCreationInfos); + mockMvc.perform(post(getNetworkModificationUri()).content(vscCreationInfosJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + assertLogMessage(new NetworkModificationException(CREATE_VSC_ERROR, "Vsc 'vsc1' : maximum reactive power is not set").getMessage(), + vscCreationInfos.getErrorType().name(), reportService); + + vscCreationInfos = (VscCreationInfos) buildModification(); + converterStationCreationInfos = buildConverterStationWithMinMaxReactiveLimits(); + converterStationCreationInfos.setConnectionPosition(15); + converterStationCreationInfos.setReactiveCapabilityCurve(false); + converterStationCreationInfos.setMinimumReactivePower(200.); + converterStationCreationInfos.setMaximumReactivePower(100.); + vscCreationInfos.setConverterStation1(converterStationCreationInfos); + + vscCreationInfosJson = mapper.writeValueAsString(vscCreationInfos); + mockMvc.perform(post(getNetworkModificationUri()).content(vscCreationInfosJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + assertLogMessage(new NetworkModificationException(CREATE_VSC_ERROR, "Vsc 'vsc1' : maximum reactive power is expected to be greater than or equal to minimum reactive power").getMessage(), + vscCreationInfos.getErrorType().name(), reportService); + + // invalid reactive capability curve limit + vscCreationInfos = (VscCreationInfos) buildModification(); + converterStationCreationInfos = buildConverterStationWithReactiveCapabilityCurve(); + converterStationCreationInfos.setConnectionPosition(55); + converterStationCreationInfos.getReactiveCapabilityCurvePoints().get(0).setP(Double.NaN); + vscCreationInfos.setConverterStation1(converterStationCreationInfos); + + vscCreationInfosJson = mapper.writeValueAsString(vscCreationInfos); + mockMvc.perform(post(getNetworkModificationUri()).content(vscCreationInfosJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + assertLogMessage(new NetworkModificationException(CREATE_VSC_ERROR, "Vsc 'vsc1' : P is not set in a reactive capability curve limits point").getMessage(), + vscCreationInfos.getErrorType().name(), reportService); + + // try to create an existing vsc + vscCreationInfos = (VscCreationInfos) buildModification(); + vscCreationInfos.setEquipmentId("hvdcLine"); + vscCreationInfosJson = mapper.writeValueAsString(vscCreationInfos); + mockMvc.perform(post(getNetworkModificationUri()).content(vscCreationInfosJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + assertLogMessage(new NetworkModificationException(HVDC_LINE_ALREADY_EXISTS, "hvdcLine").getMessage(), + vscCreationInfos.getErrorType().name(), reportService); + } +}