Skip to content

Commit

Permalink
check null values for droop and P0 (#512)
Browse files Browse the repository at this point in the history
  • Loading branch information
EtienneLt authored Aug 26, 2024
1 parent b7dbc04 commit 4d9293c
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ public enum Type {
MODIFY_CONVERTER_STATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR),
BY_FORMULA_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR),
HVDC_LINE_NOT_FOUND(HttpStatus.NOT_FOUND),
COMPOSITE_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR);

COMPOSITE_MODIFICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR),
WRONG_HVDC_ANGLE_DROOP_ACTIVE_POWER_CONTROL(HttpStatus.BAD_REQUEST);

public final HttpStatus status;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,7 @@
import com.powsybl.commons.report.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.*;
import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControlAdder;
import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRangeAdder;
import org.gridsuite.modification.server.NetworkModificationException;
Expand All @@ -28,8 +22,8 @@
import java.util.List;
import java.util.Map;

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.*;
import static org.gridsuite.modification.server.modifications.VscModification.DROOP_ACTIVE_POWER_CONTROL_P0_DROOP_REQUIRED_ERROR_MSG;

/**
* @author Seddik Yengui <seddik.yengui at rte-france.com>
Expand All @@ -54,6 +48,20 @@ public void check(Network network) throws NetworkModificationException {

checkConverterStation(network, modificationInfos.getConverterStation1());
checkConverterStation(network, modificationInfos.getConverterStation2());
checkDroop();
}

private void checkDroop() {
// extension is not enabled => ignore check inside fields
if (!Boolean.TRUE.equals(modificationInfos.getAngleDroopActivePowerControl())) {
return;
}

// enable the extension => should verify whether all fields have been filled
if (modificationInfos.getDroop() == null || modificationInfos.getP0() == null) {
throw new NetworkModificationException(WRONG_HVDC_ANGLE_DROOP_ACTIVE_POWER_CONTROL,
String.format(DROOP_ACTIVE_POWER_CONTROL_P0_DROOP_REQUIRED_ERROR_MSG));
}
}

private void checkConverterStation(Network network,
Expand Down Expand Up @@ -100,20 +108,15 @@ public void apply(Network network, ReportNode subReportNode) {
.add();
}

if (modificationInfos.getDroop() != null ||
modificationInfos.getP0() != null) {
if (shouldCreateDroopActivePowerControlExtension()) {
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.withP0(modificationInfos.getP0());
activePowerControlExtension.withDroop(modificationInfos.getDroop());

activePowerControlExtension.add();
}

reportHvdcLineInfos(subReportNode);

subReportNode.newReportNode()
Expand All @@ -130,6 +133,12 @@ public void apply(Network network, ReportNode subReportNode) {
PropertiesUtils.applyProperties(hvdcLine, subReportNode, modificationInfos.getProperties(), "VscProperties");
}

private boolean shouldCreateDroopActivePowerControlExtension() {
return Boolean.TRUE.equals(modificationInfos.getAngleDroopActivePowerControl()) &&
modificationInfos.getDroop() != null &&
modificationInfos.getP0() != null;
}

private void reportHvdcLineInfos(ReportNode subReportNode) {
List<ReportNode> characteristicsReports = new ArrayList<>();
characteristicsReports.add(ModificationUtils.getInstance().buildCreationReport(modificationInfos.getNominalV(), "DC nominal voltage"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@
import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControlAdder;
import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRange;
import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRangeAdder;
import io.micrometer.common.lang.NonNull;
import org.gridsuite.modification.server.NetworkModificationException;
import org.gridsuite.modification.server.dto.ConverterStationModificationInfos;
import org.gridsuite.modification.server.dto.ReactiveCapabilityCurveModificationInfos;
import org.gridsuite.modification.server.dto.VscModificationInfos;

import javax.annotation.Nonnull;
import java.util.*;

import static org.gridsuite.modification.server.NetworkModificationException.Type.MODIFY_BATTERY_ERROR;
import static org.gridsuite.modification.server.NetworkModificationException.Type.MODIFY_VSC_ERROR;
import static org.gridsuite.modification.server.NetworkModificationException.Type.*;
import static org.gridsuite.modification.server.modifications.VscCreation.VSC_CHARACTERISTICS;
import static org.gridsuite.modification.server.modifications.VscCreation.VSC_SETPOINTS;

Expand All @@ -31,14 +30,20 @@
*/

public class VscModification extends AbstractModification {
public static final String NO_VALUE = "No value";
public static final String ANGLE_DROOP_ACTIVE_POWER_CONTROL_FIELD = "AngleDroopActivePowerControl";
public static final String DROOP_FIELD = "Droop";
public static final String P0_FIELD = "P0";
public static final String DROOP_ACTIVE_POWER_CONTROL_P0_DROOP_REQUIRED_ERROR_MSG = "Both Droop and P0 are required when angle droop active power control is activated";
public static final String DROOP_ACTIVE_POWER_CONTROL_P0_REQUIRED_ERROR_MSG = "P0 is required when Droop is provided";

private final VscModificationInfos modificationInfos;
private static final String NO_VALUE = "No value";

public VscModification(VscModificationInfos vscModificationInfos) {
this.modificationInfos = vscModificationInfos;
}

protected void checkConverterStation(@NonNull ConverterStationModificationInfos converterStationModificationInfos, @NonNull VscConverterStation vscConverterStation) {
protected void checkConverterStation(@Nonnull ConverterStationModificationInfos converterStationModificationInfos, @Nonnull VscConverterStation vscConverterStation) {
String errorMessage = "Converter station '" + converterStationModificationInfos.getEquipmentId() + "' : ";
ModificationUtils.getInstance().checkReactiveLimit(vscConverterStation, converterStationModificationInfos.getMinQ(), converterStationModificationInfos.getMaxQ(),
converterStationModificationInfos.getReactiveCapabilityCurvePoints(), MODIFY_VSC_ERROR, errorMessage);
Expand All @@ -57,15 +62,32 @@ public void check(Network network) throws NetworkModificationException {
VscConverterStation converterStation2 = ModificationUtils.getInstance().getVscConverterStation(network, hvdcLine.getConverterStation2().getId());
checkConverterStation(modificationInfos.getConverterStation1(), converterStation1);
checkConverterStation(modificationInfos.getConverterStation2(), converterStation2);
checkDroopModification(hvdcLine);
}

checkDroopModification();
private void checkDroopModification(HvdcLine hvdcLine) {
// the extension already exists
HvdcAngleDroopActivePowerControl hvdcAngleDroopActivePowerControl = hvdcLine.getExtension(HvdcAngleDroopActivePowerControl.class);
if (hvdcAngleDroopActivePowerControl != null) {
// if droop is set p0 should be also
if (modificationInfos.getDroop() != null && modificationInfos.getP0() == null) {
throw new NetworkModificationException(WRONG_HVDC_ANGLE_DROOP_ACTIVE_POWER_CONTROL,
String.format(DROOP_ACTIVE_POWER_CONTROL_P0_REQUIRED_ERROR_MSG));
}
return;
}

}
// the extension doesn't exist yet and the modification wants to enable the extension =>
// should verify whether all fields have been filled
boolean isEnabledAngleDroopActivePowerControl = modificationInfos.getAngleDroopActivePowerControl() != null
&& Boolean.TRUE.equals(modificationInfos.getAngleDroopActivePowerControl().getValue());
if (!isEnabledAngleDroopActivePowerControl) {
return;
}

private void checkDroopModification() {
// if droop is set p0 should be also
if (modificationInfos.getP0() == null && modificationInfos.getDroop() != null) {
throw new NetworkModificationException(MODIFY_VSC_ERROR, "P0 is required to modify the equipment");
if (modificationInfos.getDroop() == null || modificationInfos.getP0() == null) {
throw new NetworkModificationException(WRONG_HVDC_ANGLE_DROOP_ACTIVE_POWER_CONTROL,
String.format(DROOP_ACTIVE_POWER_CONTROL_P0_DROOP_REQUIRED_ERROR_MSG));
}
}

Expand All @@ -75,7 +97,7 @@ public void apply(Network network, ReportNode subReportNode) {
modifyVsc(network, hvdcLine, modificationInfos, subReportNode);
}

private void modifyVsc(@NonNull Network network, @NonNull HvdcLine hvdcLine, VscModificationInfos modificationInfos, ReportNode subReportNode) {
private void modifyVsc(@Nonnull Network network, @Nonnull HvdcLine hvdcLine, VscModificationInfos modificationInfos, ReportNode subReportNode) {
subReportNode.newReportNode()
.withMessageTemplate("VscModification", "Vsc with id=${id} modified :")
.withUntypedValue("id", modificationInfos.getEquipmentId())
Expand Down Expand Up @@ -196,63 +218,57 @@ private static void createOperatorActiveRangeExt(HvdcLine hvdcLine, VscModificat
}

private void modifyExistingHvdcAngleDroopActivePowerControl(HvdcAngleDroopActivePowerControl hvdcAngleDroopActivePowerControl, List<ReportNode> reports) {
var isEnabled = hvdcAngleDroopActivePowerControl.isEnabled();
var oldDroop = hvdcAngleDroopActivePowerControl.getDroop();
var oldP0 = hvdcAngleDroopActivePowerControl.getP0();
Optional.ofNullable(modificationInfos.getAngleDroopActivePowerControl()).ifPresent(info -> {
if (info.getValue() == null) {
return;
}
hvdcAngleDroopActivePowerControl.setEnabled(info.getValue());
reports.add(ModificationUtils.getInstance().buildModificationReport(isEnabled, info.getValue(), "AngleDroopActivePowerControl"));
});

Optional.ofNullable(modificationInfos.getDroop()).ifPresent(info -> {
hvdcAngleDroopActivePowerControl.setDroop(info.getValue());
reports.add(ModificationUtils.getInstance().buildModificationReport(oldDroop, info.getValue(), "Droop"));
});

Optional.ofNullable(modificationInfos.getP0()).ifPresent(info -> {
hvdcAngleDroopActivePowerControl.setP0(info.getValue());
reports.add(ModificationUtils.getInstance().buildModificationReport(oldP0, info.getValue(), "P0"));
});
Optional.ofNullable(modificationInfos.getAngleDroopActivePowerControl()).ifPresent(modification ->
reports.add(ModificationUtils.getInstance().applyAndBuildModificationReport(
hvdcAngleDroopActivePowerControl::setEnabled,
hvdcAngleDroopActivePowerControl::isEnabled,
modification,
ANGLE_DROOP_ACTIVE_POWER_CONTROL_FIELD)));

Optional.ofNullable(modificationInfos.getDroop()).ifPresent(modification ->
reports.add(ModificationUtils.getInstance().applyAndBuildModificationReport(
hvdcAngleDroopActivePowerControl::setDroop,
hvdcAngleDroopActivePowerControl::getDroop,
modification,
DROOP_FIELD)));

Optional.ofNullable(modificationInfos.getP0()).ifPresent(modification ->
reports.add(ModificationUtils.getInstance().applyAndBuildModificationReport(
hvdcAngleDroopActivePowerControl::setP0,
hvdcAngleDroopActivePowerControl::getP0,
modification,
P0_FIELD)));
}

protected boolean checkIfChangeRequestedOnDropActiveControl() {
return modificationInfos.getAngleDroopActivePowerControl() == null
&& modificationInfos.getDroop() == null
&& modificationInfos.getP0() == null;
private boolean shouldCreateDroopActivePowerControlExtension() {
return modificationInfos.getAngleDroopActivePowerControl() != null &&
modificationInfos.getAngleDroopActivePowerControl().getValue() &&
modificationInfos.getDroop() != null &&
modificationInfos.getP0() != null;
}

private List<ReportNode> hvdcAngleDroopActivePowerControlAdder(HvdcLine hvdcLine) {
List<ReportNode> reports = new ArrayList<>();
var hvdcAngleDroopActivePowerControl = hvdcLine.getExtension(HvdcAngleDroopActivePowerControl.class);
if (hvdcAngleDroopActivePowerControl != null) {
modifyExistingHvdcAngleDroopActivePowerControl(hvdcAngleDroopActivePowerControl, reports);
} else {
var activePowerControlExtension = hvdcLine.newExtension(HvdcAngleDroopActivePowerControlAdder.class);
} else if (shouldCreateDroopActivePowerControlExtension()) {
HvdcAngleDroopActivePowerControlAdder hvdcAngleDroopActivePowerControlAdder =
hvdcLine.newExtension(HvdcAngleDroopActivePowerControlAdder.class);

if (checkIfChangeRequestedOnDropActiveControl()) {
return Collections.emptyList();
}
boolean isEnabled = modificationInfos.getAngleDroopActivePowerControl() != null && modificationInfos.getAngleDroopActivePowerControl().getValue();
if (modificationInfos.getAngleDroopActivePowerControl() != null) {
activePowerControlExtension.withEnabled(isEnabled);
reports.add(ModificationUtils.getInstance().buildModificationReport(null, isEnabled, "AngleDroopActivePowerControl"));
}
Boolean isEnabled = modificationInfos.getAngleDroopActivePowerControl().getValue();
hvdcAngleDroopActivePowerControlAdder.withEnabled(isEnabled);
reports.add(ModificationUtils.getInstance().buildModificationReport(null, isEnabled, ANGLE_DROOP_ACTIVE_POWER_CONTROL_FIELD));

var droop = modificationInfos.getDroop() != null ? modificationInfos.getDroop().getValue() : Float.NaN;
activePowerControlExtension.withDroop(droop);
if (modificationInfos.getDroop() != null) {
reports.add(ModificationUtils.getInstance().buildModificationReport(Float.NaN, droop, "Droop"));
}
var p0 = modificationInfos.getP0() != null ? modificationInfos.getP0().getValue() : Float.NaN;
activePowerControlExtension.withP0(p0);
if (modificationInfos.getP0() != null) {
reports.add(ModificationUtils.getInstance().buildModificationReport(Float.NaN, p0, "P0"));
}
activePowerControlExtension.add();
Float droop = modificationInfos.getDroop().getValue();
hvdcAngleDroopActivePowerControlAdder.withDroop(droop);
reports.add(ModificationUtils.getInstance().buildModificationReport(Float.NaN, droop, DROOP_FIELD));

Float p0 = modificationInfos.getP0().getValue();
hvdcAngleDroopActivePowerControlAdder.withP0(p0);
reports.add(ModificationUtils.getInstance().buildModificationReport(Float.NaN, p0, P0_FIELD));

hvdcAngleDroopActivePowerControlAdder.add();
}
return reports;
}
Expand Down Expand Up @@ -340,5 +356,4 @@ private void modifyVscReactiveLimitsAttributes(ConverterStationModificationInfos
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import java.util.Map;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.gridsuite.modification.server.NetworkModificationException.Type.*;
import static org.gridsuite.modification.server.modifications.VscModification.DROOP_ACTIVE_POWER_CONTROL_P0_DROOP_REQUIRED_ERROR_MSG;
import static org.gridsuite.modification.server.utils.TestUtils.assertLogMessage;
import static org.junit.Assert.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
Expand Down Expand Up @@ -60,7 +62,7 @@ protected ModificationInfos buildModification() {
.operatorActivePowerLimitFromSide1ToSide2(6.0F)
.operatorActivePowerLimitFromSide2ToSide1(8F)
.droop(1F)
.angleDroopActivePowerControl(false)
.angleDroopActivePowerControl(true)
.converterStation1(buildConverterStationWithReactiveCapabilityCurve())
.converterStation2(buildConverterStationWithMinMaxReactiveLimits())
.properties(List.of(FreePropertyInfos.builder().name(PROPERTY_NAME).value(PROPERTY_VALUE).build()))
Expand Down Expand Up @@ -298,4 +300,34 @@ public void testCreateWithErrors() throws Exception {
assertLogMessage(new NetworkModificationException(HVDC_LINE_ALREADY_EXISTS, "hvdcLine").getMessage(),
vscCreationInfos.getErrorType().name(), reportService);
}

@Test
public void testCreateWithoutEnablingDroopPowerControl() throws Exception {
// create without enabling droop power control
VscCreationInfos vscCreationInfos = (VscCreationInfos) buildModification();
vscCreationInfos.setAngleDroopActivePowerControl(false);
String vscCreationInfosJson = mapper.writeValueAsString(vscCreationInfos);
mockMvc.perform(post(getNetworkModificationUri()).content(vscCreationInfosJson).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
assertThat(getNetwork().getHvdcLine("vsc1")).isNotNull();
HvdcLine hvdcLine = getNetwork().getHvdcLine("vsc1");
assertThat(hvdcLine).isNotNull();
HvdcAngleDroopActivePowerControl activePowerControl = hvdcLine.getExtension(HvdcAngleDroopActivePowerControl.class);
assertThat(activePowerControl).isNull();
}

@Test
public void testCreateWithEnablingDroopPowerControl() throws Exception {
// create with enabling droop power control but not provide Droop and P0
VscCreationInfos vscCreationInfos = (VscCreationInfos) buildModification();
vscCreationInfos.setAngleDroopActivePowerControl(true);
vscCreationInfos.setDroop(null);
vscCreationInfos.setP0(null);
String vscCreationInfosJson = mapper.writeValueAsString(vscCreationInfos);
mockMvc.perform(post(getNetworkModificationUri()).content(vscCreationInfosJson).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
assertLogMessage(new NetworkModificationException(WRONG_HVDC_ANGLE_DROOP_ACTIVE_POWER_CONTROL,
String.format(DROOP_ACTIVE_POWER_CONTROL_P0_DROOP_REQUIRED_ERROR_MSG)).getMessage(),
vscCreationInfos.getErrorType().name(), reportService);
}
}
Loading

0 comments on commit 4d9293c

Please sign in to comment.