Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loss of a VSC converter station after contingency #961

Merged
merged 7 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,7 @@ static void updateBranchEquations(LfBranch branch) {
private static void createHvdcAcEmulationEquations(LfHvdc hvdc, EquationSystem<AcVariableType, AcEquationType> equationSystem) {
EquationTerm<AcVariableType, AcEquationType> p1 = null;
EquationTerm<AcVariableType, AcEquationType> p2 = null;
if (hvdc.getBus1() != null && hvdc.getBus2() != null) {
if (hvdc.getBus1() != null && hvdc.getBus2() != null && hvdc.isAcEmulation()) {
p1 = new HvdcAcEmulationSide1ActiveFlowEquationTerm(hvdc, hvdc.getBus1(), hvdc.getBus2(), equationSystem.getVariableSet());
p2 = new HvdcAcEmulationSide2ActiveFlowEquationTerm(hvdc, hvdc.getBus1(), hvdc.getBus2(), equationSystem.getVariableSet());
} else {
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/powsybl/openloadflow/network/HvdcState.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,20 @@
*/
public class HvdcState extends ElementState<LfHvdc> {

private boolean acEmulation;

public HvdcState(LfHvdc hvdc) {
super(hvdc);
this.acEmulation = hvdc.isAcEmulation();
}

public static HvdcState save(LfHvdc hvdc) {
return new HvdcState(hvdc);
}

@Override
public void restore() {
super.restore();
element.setAcEmulation(acEmulation);
}
}
7 changes: 4 additions & 3 deletions src/main/java/com/powsybl/openloadflow/network/LfAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -365,9 +365,10 @@ public void apply(LfNetworkParameters networkParameters) {
}

if (hvdc != null) {
hvdc.setDisabled(true);
hvdc.getConverterStation1().setTargetP(-hvdc.getP1().eval());
hvdc.getConverterStation2().setTargetP(-hvdc.getP2().eval());
hvdc.setAcEmulation(false);
hvdc.setDisabled(true); // for equations only, but should be hidden
hvdc.getConverterStation1().setTargetP(-hvdc.getP1().eval()); // override
hvdc.getConverterStation2().setTargetP(-hvdc.getP2().eval()); // override
}
}
}
2 changes: 2 additions & 0 deletions src/main/java/com/powsybl/openloadflow/network/LfBus.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ default double getHighVoltageLimit() {

void addBranch(LfBranch branch);

List<LfHvdc> getHvdcs();

void addHvdc(LfHvdc hvdc);

void updateState(LfNetworkStateUpdateParameters parameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,18 @@ public class LfContingency {

private final Set<String> disconnectedElementIds;

private final Set<LfHvdc> hvdcsWithoutPower;

public LfContingency(String id, int index, int createdSynchronousComponentsCount, DisabledNetwork disabledNetwork, Map<LfShunt, AdmittanceShift> shuntsShift,
Map<LfLoad, LfLostLoad> lostLoads, Set<LfGenerator> lostGenerators) {
Map<LfLoad, LfLostLoad> lostLoads, Set<LfGenerator> lostGenerators, Set<LfHvdc> hvdcsWithoutPower) {
this.id = Objects.requireNonNull(id);
this.index = index;
this.createdSynchronousComponentsCount = createdSynchronousComponentsCount;
this.disabledNetwork = Objects.requireNonNull(disabledNetwork);
this.shuntsShift = Objects.requireNonNull(shuntsShift);
this.lostLoads = Objects.requireNonNull(lostLoads);
this.lostGenerators = Objects.requireNonNull(lostGenerators);
this.hvdcsWithoutPower = Objects.requireNonNull(hvdcsWithoutPower);
this.disconnectedLoadActivePower = 0.0;
this.disconnectedGenerationActivePower = 0.0;
this.disconnectedElementIds = new HashSet<>();
Expand Down Expand Up @@ -179,6 +182,10 @@ public void apply(LoadFlowParameters.BalanceType balanceType) {
bus.setGeneratorReactivePowerControlEnabled(false);
}
}
for (LfHvdc hvdc : hvdcsWithoutPower) {
hvdc.getConverterStation1().setTargetP(0.0);
hvdc.getConverterStation2().setTargetP(0.0);
}
}

private static double getUpdatedLoadP0(LfLoad load, LoadFlowParameters.BalanceType balanceType, double initialP0, double initialVariableActivePower) {
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/powsybl/openloadflow/network/LfHvdc.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public interface LfHvdc extends LfElement {

LfBus getBus2();

LfBus getOtherBus(LfBus bus);

void setP1(Evaluable p1);

Evaluable getP1();
Expand All @@ -29,6 +31,10 @@ public interface LfHvdc extends LfElement {

double getP0();

boolean isAcEmulation();

void setAcEmulation(boolean acEmulation);

LfVscConverterStation getConverterStation1();

LfVscConverterStation getConverterStation2();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,11 @@ public void addBranch(LfBranch branch) {
branches.add(Objects.requireNonNull(branch));
}

@Override
public List<LfHvdc> getHvdcs() {
return hvdcs;
}

@Override
public void addHvdc(LfHvdc hvdc) {
hvdcs.add(Objects.requireNonNull(hvdc));
Expand Down Expand Up @@ -711,6 +716,14 @@ public void setDisabled(boolean disabled) {
if (controllerShunt != null) {
controllerShunt.setDisabled(disabled);
}
for (LfHvdc hvdc : hvdcs) {
if (disabled) {
hvdc.setDisabled(true);
} else if (!hvdc.getOtherBus(this).isDisabled()) {
// if both buses enabled only
hvdc.setDisabled(false);
}
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,27 @@ public class LfHvdcImpl extends AbstractElement implements LfHvdc {

private Evaluable p2 = NAN;

private final double droop;
private double droop = Double.NaN;

private final double p0;
private double p0 = Double.NaN;

private LfVscConverterStation converterStation1;

private LfVscConverterStation converterStation2;

public LfHvdcImpl(String id, LfBus bus1, LfBus bus2, LfNetwork network, HvdcAngleDroopActivePowerControl control) {
private boolean acEmulation;

public LfHvdcImpl(String id, LfBus bus1, LfBus bus2, LfNetwork network, HvdcAngleDroopActivePowerControl control,
boolean acEmulation) {
super(network);
this.id = Objects.requireNonNull(id);
this.bus1 = bus1;
this.bus2 = bus2;
Objects.requireNonNull(control);
droop = control.getDroop();
p0 = control.getP0();
this.acEmulation = acEmulation && control != null && control.isEnabled();
if (this.acEmulation) {
droop = control.getDroop();
p0 = control.getP0();
}
}

@Override
Expand All @@ -68,6 +73,21 @@ public LfBus getBus2() {
return this.bus2;
}

@Override
public LfBus getOtherBus(LfBus bus) {
return bus.equals(bus1) ? bus2 : bus1;
}

@Override
public void setDisabled(boolean disabled) {
super.setDisabled(disabled); // for AC emulation equations only.
if (!acEmulation && !disabled) {
// re-active power transmission to initial target values.
converterStation1.setTargetP(converterStation1.getInitialTargetP());
converterStation2.setTargetP(converterStation2.getInitialTargetP());
}
}

@Override
public void setP1(Evaluable p1) {
this.p1 = Objects.requireNonNull(p1);
Expand Down Expand Up @@ -98,6 +118,16 @@ public double getP0() {
return p0 / PerUnit.SB;
}

@Override
public boolean isAcEmulation() {
return acEmulation;
}

@Override
public void setAcEmulation(boolean acEmulation) {
this.acEmulation = acEmulation;
}

@Override
public LfVscConverterStation getConverterStation1() {
return converterStation1;
Expand All @@ -122,7 +152,9 @@ public void setConverterStation2(LfVscConverterStation converterStation2) {

@Override
public void updateState() {
((LfVscConverterStationImpl) converterStation1).getStation().getTerminal().setP(p1.eval() * PerUnit.SB);
((LfVscConverterStationImpl) converterStation2).getStation().getTerminal().setP(p2.eval() * PerUnit.SB);
if (acEmulation) {
((LfVscConverterStationImpl) converterStation1).getStation().getTerminal().setP(p1.eval() * PerUnit.SB);
((LfVscConverterStationImpl) converterStation2).getStation().getTerminal().setP(p2.eval() * PerUnit.SB);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -482,23 +482,19 @@ private static void createBranches(List<LfBus> lfBuses, LfNetwork lfNetwork, LfT
}
}

if (parameters.isHvdcAcEmulation()) {
for (HvdcLine hvdcLine : loadingContext.hvdcLineSet) {
HvdcAngleDroopActivePowerControl control = hvdcLine.getExtension(HvdcAngleDroopActivePowerControl.class);
if (control != null && control.isEnabled()) {
LfBus lfBus1 = getLfBus(hvdcLine.getConverterStation1().getTerminal(), lfNetwork, parameters.isBreakers());
LfBus lfBus2 = getLfBus(hvdcLine.getConverterStation2().getTerminal(), lfNetwork, parameters.isBreakers());
LfVscConverterStationImpl cs1 = (LfVscConverterStationImpl) lfNetwork.getGeneratorById(hvdcLine.getConverterStation1().getId());
LfVscConverterStationImpl cs2 = (LfVscConverterStationImpl) lfNetwork.getGeneratorById(hvdcLine.getConverterStation2().getId());
if (cs1 != null && cs2 != null) {
LfHvdc lfHvdc = new LfHvdcImpl(hvdcLine.getId(), lfBus1, lfBus2, lfNetwork, control);
lfHvdc.setConverterStation1((LfVscConverterStationImpl) lfNetwork.getGeneratorById(hvdcLine.getConverterStation1().getId()));
lfHvdc.setConverterStation2((LfVscConverterStationImpl) lfNetwork.getGeneratorById(hvdcLine.getConverterStation2().getId()));
lfNetwork.addHvdc(lfHvdc);
} else {
LOGGER.warn("Hvdc line '{}' in AC emulation but converter stations are not in the same synchronous component: operated using active set point.", hvdcLine.getId());
}
}
for (HvdcLine hvdcLine : loadingContext.hvdcLineSet) {
LfBus lfBus1 = getLfBus(hvdcLine.getConverterStation1().getTerminal(), lfNetwork, parameters.isBreakers());
LfBus lfBus2 = getLfBus(hvdcLine.getConverterStation2().getTerminal(), lfNetwork, parameters.isBreakers());
LfVscConverterStationImpl cs1 = (LfVscConverterStationImpl) lfNetwork.getGeneratorById(hvdcLine.getConverterStation1().getId());
LfVscConverterStationImpl cs2 = (LfVscConverterStationImpl) lfNetwork.getGeneratorById(hvdcLine.getConverterStation2().getId());
HvdcAngleDroopActivePowerControl control = hvdcLine.getExtension(HvdcAngleDroopActivePowerControl.class);
if (cs1 != null && cs2 != null) {
LfHvdc lfHvdc = new LfHvdcImpl(hvdcLine.getId(), lfBus1, lfBus2, lfNetwork, control, parameters.isHvdcAcEmulation());
lfHvdc.setConverterStation1(cs1);
lfHvdc.setConverterStation2(cs2);
lfNetwork.addHvdc(lfHvdc);
} else {
LOGGER.warn("The converter stations of hvdc line {} are not in the same synchronous component: no hvdc link created to model active power flow.", hvdcLine.getId());
}
}
}
Expand Down
annetill marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void setHvdc(LfHvdc hvdc) {
@Override
public double getTargetP() {
// because in case of AC emulation, active power is injected by HvdcAcEmulationSideXActiveFlowEquationTerm equations
return (hvdc == null || hvdc.isDisabled()) ? super.getTargetP() : 0;
return hvdc == null || !hvdc.isAcEmulation() ? super.getTargetP() : 0;
}

@Override
Expand Down Expand Up @@ -96,7 +96,7 @@ public void updateState(LfNetworkStateUpdateParameters parameters) {
var station = getStation();
station.getTerminal()
.setQ(Double.isNaN(calculatedQ) ? -station.getReactivePowerSetpoint() : -calculatedQ * PerUnit.SB);
if (hvdc == null) { // because when AC emulation is activated, update of p is done in LFHvdcImpl
if (hvdc == null || !hvdc.isAcEmulation()) { // because when AC emulation is activated, update of p is done in LFHvdcImpl
station.getTerminal().setP(-targetP * PerUnit.SB);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import com.powsybl.contingency.Contingency;
import com.powsybl.contingency.ContingencyElement;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControl;
import com.powsybl.iidm.network.extensions.LoadDetail;
import com.powsybl.iidm.network.util.HvdcUtils;
import com.powsybl.openloadflow.graph.GraphConnectivity;
Expand Down Expand Up @@ -43,7 +42,7 @@ public class PropagatedContingency {

private final Map<String, DisabledBranchStatus> branchIdsToOpen = new LinkedHashMap<>();

private final Set<String> hvdcIdsToOpen = new HashSet<>(); // for HVDC in AC emulation
private final Set<String> hvdcIdsToOpen = new HashSet<>();

private final Set<String> generatorIdsToLose = new HashSet<>();

Expand All @@ -67,14 +66,6 @@ public Map<String, DisabledBranchStatus> getBranchIdsToOpen() {
return branchIdsToOpen;
}

public Set<Switch> getSwitchesToOpen() {
return switchesToOpen;
}

public Set<String> getHvdcIdsToOpen() {
return hvdcIdsToOpen;
}

public Set<String> getGeneratorIdsToLose() {
return generatorIdsToLose;
}
Expand All @@ -83,10 +74,6 @@ public Map<String, PowerShift> getLoadIdsToLoose() {
return loadIdsToLoose;
}

public Map<String, AdmittanceShift> getShuntIdsToShift() {
return shuntIdsToShift;
}

public PropagatedContingency(Contingency contingency, int index, Set<Switch> switchesToOpen, Set<Terminal> terminalsToDisconnect,
Set<String> busIdsToLose) {
this.contingency = Objects.requireNonNull(contingency);
Expand Down Expand Up @@ -248,13 +235,11 @@ private void complete(LfTopoConfig topoConfig, PropagatedContingencyCreationPara
break;

case HVDC_CONVERTER_STATION:
// in case of a hvdc contingency, both converter station will go through this case.
// in case of the lost of one VSC converter station only, the transmission of active power is stopped
// but the other converter station, if present, keeps it voltage control if present.
HvdcConverterStation<?> station = (HvdcConverterStation<?>) connectable;
HvdcAngleDroopActivePowerControl control = station.getHvdcLine().getExtension(HvdcAngleDroopActivePowerControl.class);
if (control != null && control.isEnabled() && creationParameters.isHvdcAcEmulation()) {
hvdcIdsToOpen.add(station.getHvdcLine().getId());
}
// FIXME
// the other converter station should be considered to if in the same synchronous component (hvdc setpoint mode).
hvdcIdsToOpen.add(station.getHvdcLine().getId());
if (connectable instanceof VscConverterStation) {
generatorIdsToLose.add(connectable.getId());
} else {
Expand Down Expand Up @@ -400,7 +385,7 @@ private Map<LfBranch, DisabledBranchStatus> findBranchToOpenDirectlyImpactedByCo
return branchesToOpen;
}

record ContingencyConnectivityLossImpact(boolean ok, int createdSynchronousComponents, Set<LfBus> busesToLost) {
record ContingencyConnectivityLossImpact(boolean ok, int createdSynchronousComponents, Set<LfBus> busesToLost, Set<LfHvdc> hvdcsWithoutPower) {
}

private ContingencyConnectivityLossImpact findBusesAndBranchesImpactedBecauseOfConnectivityLoss(LfNetwork network, Map<LfBranch, DisabledBranchStatus> branchesToOpen) {
Expand All @@ -417,15 +402,29 @@ private ContingencyConnectivityLossImpact findBusesAndBranchesImpactedBecauseOfC
// If a contingency leads to an isolated slack bus, this bus is considered as the main component.
// In that case, we have an issue with a different number of variables and equations.
LOGGER.error("Contingency '{}' leads to an isolated slack bus: not supported", contingency.getId());
return new ContingencyConnectivityLossImpact(false, 0, Collections.emptySet());
return new ContingencyConnectivityLossImpact(false, 0, Collections.emptySet(), Collections.emptySet());
}

// add to contingency description buses and branches that won't be part of the main connected
// component in post contingency state
int createdSynchronousComponents = connectivity.getNbConnectedComponents() - 1;
Set<LfBus> busesToLost = connectivity.getVerticesRemovedFromMainComponent();

return new ContingencyConnectivityLossImpact(true, createdSynchronousComponents, busesToLost);
// as we know here the connectivity after contingency, we have to reset active power flow of a hvdc line
// if one bus of the line is lost.
Set<LfHvdc> hvdcsWithoutFlow = new HashSet<>();
for (LfHvdc hvdcLine : network.getHvdcs()) {
if (busesToLost.contains(hvdcLine.getBus1()) && !busesToLost.contains(hvdcLine.getBus2())
&& connectivity.getConnectedComponent(hvdcLine.getBus1()).size() == 1) {
hvdcsWithoutFlow.add(hvdcLine);
}
if (busesToLost.contains(hvdcLine.getBus2()) && !busesToLost.contains(hvdcLine.getBus1())
&& connectivity.getConnectedComponent(hvdcLine.getBus2()).size() == 1) {
hvdcsWithoutFlow.add(hvdcLine);
}
}

return new ContingencyConnectivityLossImpact(true, createdSynchronousComponents, busesToLost, hvdcsWithoutFlow);
} finally {
// reset connectivity to discard triggered elements
connectivity.undoTemporaryChanges();
Expand Down Expand Up @@ -509,9 +508,6 @@ public Optional<LfContingency> toLfContingency(LfNetwork network) {
.collect(Collectors.toCollection(LinkedHashSet::new));

for (LfHvdc hvdcLine : network.getHvdcs()) {
// FIXME
// if we loose a bus with a converter station, the other converter station should be considered to if in the
// same synchronous component (hvdc setpoint mode).
if (busesToLost.contains(hvdcLine.getBus1()) || busesToLost.contains(hvdcLine.getBus2())) {
lostHvdcs.add(hvdcLine);
}
Expand All @@ -522,11 +518,14 @@ public Optional<LfContingency> toLfContingency(LfNetwork network) {
&& shunts.isEmpty()
&& loads.isEmpty()
&& generators.isEmpty()
&& lostHvdcs.isEmpty()) {
&& lostHvdcs.isEmpty()
&& connectivityLossImpact.hvdcsWithoutPower().isEmpty()) {
LOGGER.debug("Contingency '{}' has no impact", contingency.getId());
return Optional.empty();
}

return Optional.of(new LfContingency(contingency.getId(), index, connectivityLossImpact.createdSynchronousComponents, new DisabledNetwork(busesToLost, branchesToOpen, lostHvdcs), shunts, loads, generators));
return Optional.of(new LfContingency(contingency.getId(), index, connectivityLossImpact.createdSynchronousComponents,
new DisabledNetwork(busesToLost, branchesToOpen, lostHvdcs), shunts, loads, generators,
connectivityLossImpact.hvdcsWithoutPower()));
}
}
Loading