-
Notifications
You must be signed in to change notification settings - Fork 8
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
Support of LoadAction in AC and DC security analysis #660
Changes from 8 commits
f6f6931
967f29f
4009130
6c01717
df6168a
d37adc8
2bafb8d
a772476
b86c9b8
e2ec3cc
ed184c0
dc14b0f
9db3a94
deb6e05
69f2d25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,11 +6,15 @@ | |
*/ | ||
package com.powsybl.openloadflow.network; | ||
|
||
import com.powsybl.iidm.network.Bus; | ||
import com.powsybl.iidm.network.Load; | ||
import com.powsybl.iidm.network.Network; | ||
import com.powsybl.iidm.network.Terminal; | ||
import com.powsybl.loadflow.LoadFlowParameters; | ||
import com.powsybl.openloadflow.graph.GraphConnectivity; | ||
import com.powsybl.security.action.Action; | ||
import com.powsybl.security.action.LineConnectionAction; | ||
import com.powsybl.security.action.PhaseTapChangerTapPositionAction; | ||
import com.powsybl.security.action.SwitchAction; | ||
import com.powsybl.openloadflow.util.PerUnit; | ||
import com.powsybl.security.action.*; | ||
import org.apache.commons.lang3.tuple.Pair; | ||
|
||
import java.util.*; | ||
|
||
|
@@ -54,58 +58,93 @@ public boolean isRelative() { | |
|
||
private final TapPositionChange tapPositionChange; | ||
|
||
private LfAction(String id, LfBranch disabledBranch, LfBranch enabledBranch, TapPositionChange tapPositionChange) { | ||
private final Map<Pair<LfBus, String>, Pair<Double, PowerShift>> busesLoadShift; // key: bus and loadId. | ||
annetill marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
private LfAction(String id, LfBranch disabledBranch, LfBranch enabledBranch, TapPositionChange tapPositionChange, Map<Pair<LfBus, String>, Pair<Double, PowerShift>> busesLoadShift) { | ||
this.id = Objects.requireNonNull(id); | ||
this.disabledBranch = disabledBranch; | ||
this.enabledBranch = enabledBranch; | ||
this.tapPositionChange = tapPositionChange; | ||
this.busesLoadShift = busesLoadShift; | ||
} | ||
|
||
public static Optional<LfAction> create(Action action, LfNetwork network) { | ||
public static Optional<LfAction> create(Action action, LfNetwork lfNetwork, Network network, boolean breakers) { | ||
Objects.requireNonNull(action); | ||
Objects.requireNonNull(network); | ||
switch (action.getType()) { | ||
case SwitchAction.NAME: | ||
return create((SwitchAction) action, network); | ||
return create((SwitchAction) action, lfNetwork); | ||
|
||
case LineConnectionAction.NAME: | ||
return create((LineConnectionAction) action, network); | ||
return create((LineConnectionAction) action, lfNetwork); | ||
|
||
case PhaseTapChangerTapPositionAction.NAME: | ||
return create((PhaseTapChangerTapPositionAction) action, network); | ||
return create((PhaseTapChangerTapPositionAction) action, lfNetwork); | ||
|
||
case LoadAction.NAME: | ||
return create((LoadAction) action, lfNetwork, network, breakers); | ||
|
||
default: | ||
throw new UnsupportedOperationException("Unsupported action type: " + action.getType()); | ||
} | ||
} | ||
|
||
private static Optional<LfAction> create(PhaseTapChangerTapPositionAction action, LfNetwork network) { | ||
LfBranch branch = network.getBranchById(action.getTransformerId()); // only two windings transformer for the moment. | ||
private static Optional<LfAction> create(LoadAction action, LfNetwork lfNetwork, Network network, boolean breakers) { | ||
Load load = network.getLoad(action.getLoadId()); | ||
Terminal terminal = load.getTerminal(); | ||
Bus bus = breakers ? terminal.getBusBreakerView().getBus() : terminal.getBusView().getBus(); | ||
annetill marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (bus != null) { | ||
annetill marked this conversation as resolved.
Show resolved
Hide resolved
|
||
LfBus lfBus = lfNetwork.getBusById(bus.getId()); | ||
if (!lfBus.getAggregatedLoads().isDisabled(action.getLoadId())) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure we have to check this here? |
||
double activePowerShift = 0; | ||
double reactivePowerShift = 0; | ||
Optional<Double> activePowerValue = action.getActivePowerValue(); | ||
Optional<Double> reactivePowerValue = action.getReactivePowerValue(); | ||
if (activePowerValue.isPresent()) { | ||
activePowerShift = action.isRelativeValue() ? activePowerValue.get() : activePowerValue.get() - load.getP0(); | ||
} | ||
if (reactivePowerValue.isPresent()) { | ||
reactivePowerShift = action.isRelativeValue() ? reactivePowerValue.get() : reactivePowerValue.get() - load.getQ0(); | ||
} | ||
// In case of a power shift, we suppose that the shift on a load P0 is exactly the same on the variable active power | ||
// of P0 that could be described in a LoadDetail extension. | ||
PowerShift powerShift = new PowerShift(activePowerShift / PerUnit.SB, activePowerShift / PerUnit.SB, reactivePowerShift / PerUnit.SB); | ||
Map<Pair<LfBus, String>, Pair<Double, PowerShift>> busesLoadShift = new HashMap<>(); | ||
busesLoadShift.put(Pair.of(lfBus, load.getId()), Pair.of(load.getP0(), powerShift)); | ||
return Optional.of(new LfAction(action.getId(), null, null, null, busesLoadShift)); | ||
} | ||
} | ||
|
||
return Optional.empty(); // could be in another component or in contingency. | ||
} | ||
|
||
private static Optional<LfAction> create(PhaseTapChangerTapPositionAction action, LfNetwork lfNetwork) { | ||
LfBranch branch = lfNetwork.getBranchById(action.getTransformerId()); // only two windings transformer for the moment. | ||
if (branch != null) { | ||
if (branch.getPiModel() instanceof SimplePiModel) { | ||
throw new UnsupportedOperationException("Phase tap changer tap connection action: only one tap in the branch {" + action.getTransformerId() + "}"); | ||
} else { | ||
var tapPositionChange = new TapPositionChange(branch, action.getValue(), action.isRelativeValue()); | ||
return Optional.of(new LfAction(action.getId(), null, null, tapPositionChange)); | ||
return Optional.of(new LfAction(action.getId(), null, null, tapPositionChange, Collections.emptyMap())); | ||
} | ||
} | ||
return Optional.empty(); // could be in another component | ||
} | ||
|
||
private static Optional<LfAction> create(LineConnectionAction action, LfNetwork network) { | ||
LfBranch branch = network.getBranchById(action.getLineId()); | ||
private static Optional<LfAction> create(LineConnectionAction action, LfNetwork lfNetwork) { | ||
LfBranch branch = lfNetwork.getBranchById(action.getLineId()); | ||
if (branch != null) { | ||
if (action.isOpenSide1() && action.isOpenSide2()) { | ||
return Optional.of(new LfAction(action.getId(), branch, null, null)); | ||
return Optional.of(new LfAction(action.getId(), branch, null, null, Collections.emptyMap())); | ||
} else { | ||
throw new UnsupportedOperationException("Line connection action: only open line at both sides is supported yet."); | ||
} | ||
} | ||
return Optional.empty(); // could be in another component | ||
} | ||
|
||
private static Optional<LfAction> create(SwitchAction action, LfNetwork network) { | ||
LfBranch branch = network.getBranchById(action.getSwitchId()); | ||
private static Optional<LfAction> create(SwitchAction action, LfNetwork lfNetwork) { | ||
LfBranch branch = lfNetwork.getBranchById(action.getSwitchId()); | ||
if (branch != null) { | ||
LfBranch disabledBranch = null; | ||
LfBranch enabledBranch = null; | ||
|
@@ -114,7 +153,7 @@ private static Optional<LfAction> create(SwitchAction action, LfNetwork network) | |
} else { | ||
enabledBranch = branch; | ||
} | ||
return Optional.of(new LfAction(action.getId(), disabledBranch, enabledBranch, null)); | ||
return Optional.of(new LfAction(action.getId(), disabledBranch, enabledBranch, null, Collections.emptyMap())); | ||
} | ||
return Optional.empty(); // could be in another component | ||
} | ||
|
@@ -131,7 +170,7 @@ public LfBranch getEnabledBranch() { | |
return enabledBranch; | ||
} | ||
|
||
public static void apply(List<LfAction> actions, LfNetwork network, LfContingency contingency) { | ||
public static void apply(List<LfAction> actions, LfNetwork network, LfContingency contingency, LoadFlowParameters.BalanceType balanceType) { | ||
Objects.requireNonNull(actions); | ||
Objects.requireNonNull(network); | ||
|
||
|
@@ -140,7 +179,7 @@ public static void apply(List<LfAction> actions, LfNetwork network, LfContingenc | |
|
||
// then process remaining changes of actions | ||
for (LfAction action : actions) { | ||
action.apply(); | ||
action.apply(balanceType); | ||
} | ||
} | ||
|
||
|
@@ -193,13 +232,31 @@ public void updateConnectivity(GraphConnectivity<LfBus, LfBranch> connectivity) | |
} | ||
} | ||
|
||
public void apply() { | ||
public void apply(LoadFlowParameters.BalanceType balanceType) { | ||
if (tapPositionChange != null) { | ||
LfBranch branch = tapPositionChange.getBranch(); | ||
int tapPosition = branch.getPiModel().getTapPosition(); | ||
int value = tapPositionChange.getValue(); | ||
int newTapPosition = tapPositionChange.isRelative() ? tapPosition + value : value; | ||
branch.getPiModel().setTapPosition(newTapPosition); | ||
} | ||
|
||
if (!busesLoadShift.isEmpty()) { | ||
for (var e : busesLoadShift.entrySet()) { | ||
LfBus bus = e.getKey().getLeft(); | ||
String loadId = e.getKey().getRight(); | ||
if (!bus.getAggregatedLoads().isDisabled(loadId)) { | ||
Double loadP0 = e.getValue().getLeft(); // initial load P0. | ||
PowerShift shift = e.getValue().getRight(); | ||
double newP0 = loadP0 / PerUnit.SB + shift.getActive(); | ||
double oldUpdatedP0 = LfContingency.getUpdatedLoadP0(bus, balanceType, loadP0 / PerUnit.SB, loadP0 / PerUnit.SB); | ||
double newUpdatedP0 = LfContingency.getUpdatedLoadP0(bus, balanceType, newP0, newP0); | ||
bus.setLoadTargetP(bus.getLoadTargetP() + newUpdatedP0 - oldUpdatedP0); | ||
bus.setLoadTargetQ(bus.getLoadTargetQ() + shift.getReactive()); | ||
bus.getAggregatedLoads().setAbsVariableLoadTargetP(bus.getAggregatedLoads().getAbsVariableLoadTargetP() | ||
+ Math.signum(shift.getActive()) * Math.abs(shift.getVariableActive()) * PerUnit.SB); | ||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ | |
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* @author Geoffroy Jamgotchian <geoffroy.jamgotchian at rte-france.com> | ||
|
@@ -38,12 +39,14 @@ public class LfContingency { | |
|
||
private final Map<LfBus, PowerShift> busesLoadShift; | ||
|
||
private final Set<String> originalPowerShiftIds; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At first reading it was really not clear to me that this is load IDs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not only load ids but also the other equipments converted as power shift as LCC converter stations. |
||
|
||
private final Set<LfGenerator> lostGenerators; | ||
|
||
private double activePowerLoss = 0; | ||
|
||
public LfContingency(String id, int index, Set<LfBus> disabledBuses, Set<LfBranch> disabledBranches, Map<LfShunt, AdmittanceShift> shuntsShift, | ||
Map<LfBus, PowerShift> busesLoadShift, Set<LfGenerator> lostGenerators, Set<LfHvdc> disabledHvdcs) { | ||
Map<LfBus, PowerShift> busesLoadShift, Set<LfGenerator> lostGenerators, Set<LfHvdc> disabledHvdcs, Set<String> originalPowerShiftIds) { | ||
this.id = Objects.requireNonNull(id); | ||
this.index = index; | ||
this.disabledBuses = Objects.requireNonNull(disabledBuses); | ||
|
@@ -52,6 +55,7 @@ public LfContingency(String id, int index, Set<LfBus> disabledBuses, Set<LfBranc | |
this.shuntsShift = Objects.requireNonNull(shuntsShift); | ||
this.busesLoadShift = Objects.requireNonNull(busesLoadShift); | ||
this.lostGenerators = Objects.requireNonNull(lostGenerators); | ||
this.originalPowerShiftIds = Objects.requireNonNull(originalPowerShiftIds); | ||
for (LfBus bus : disabledBuses) { | ||
activePowerLoss += bus.getGenerationTargetP() - bus.getLoadTargetP(); | ||
} | ||
|
@@ -116,6 +120,11 @@ public void apply(LoadFlowParameters.BalanceType balanceType) { | |
bus.setLoadTargetP(bus.getLoadTargetP() - getUpdatedLoadP0(bus, balanceType, shift.getActive(), shift.getVariableActive())); | ||
bus.setLoadTargetQ(bus.getLoadTargetQ() - shift.getReactive()); | ||
bus.getAggregatedLoads().setAbsVariableLoadTargetP(bus.getAggregatedLoads().getAbsVariableLoadTargetP() - Math.abs(shift.getVariableActive()) * PerUnit.SB); | ||
Set<String> loadsIdsInContingency = originalPowerShiftIds.stream() | ||
.distinct() | ||
.filter(bus.getAggregatedLoads().getOriginalIds()::contains) // maybe not optimized. | ||
.collect(Collectors.toSet()); | ||
loadsIdsInContingency.stream().forEach(loadId -> bus.getAggregatedLoads().setDisabled(loadId, true)); | ||
} | ||
Set<LfBus> generatorBuses = new HashSet<>(); | ||
for (LfGenerator generator : lostGenerators) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,8 +12,7 @@ | |
import com.powsybl.openloadflow.network.LfAggregatedLoads; | ||
import com.powsybl.openloadflow.util.PerUnit; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
|
@@ -31,6 +30,8 @@ class LfAggregatedLoadsImpl extends AbstractPropertyBag implements LfAggregatedL | |
|
||
private boolean initialized; | ||
|
||
private Map<String, Boolean> loadsStatus = new LinkedHashMap<>(); | ||
|
||
LfAggregatedLoadsImpl(boolean distributedOnConformLoad) { | ||
this.distributedOnConformLoad = distributedOnConformLoad; | ||
} | ||
|
@@ -42,6 +43,7 @@ public List<String> getOriginalIds() { | |
|
||
void add(Load load) { | ||
loadsRefs.add(new Ref<>(load)); | ||
loadsStatus.put(load.getId(), false); | ||
initialized = false; | ||
} | ||
|
||
|
@@ -112,6 +114,26 @@ public double getLoadTargetQ(double diffLoadTargetP) { | |
return newLoadTargetQ; | ||
} | ||
|
||
@Override | ||
public boolean isDisabled(String originalId) { | ||
return loadsStatus.get(originalId); | ||
} | ||
|
||
@Override | ||
public void setDisabled(String originalId, boolean disabled) { | ||
loadsStatus.put(originalId, disabled); | ||
} | ||
|
||
@Override | ||
public Map<String, Boolean> getLoadsStatus() { | ||
return loadsStatus; | ||
} | ||
|
||
@Override | ||
public void setLoadsStatus(Map<String, Boolean> loadsStatus) { | ||
this.loadsStatus = loadsStatus; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. my understanding is that these get/set methods are used only for the purpose of saving and restoring a BusDcState. In order to avoid the map to be manipulated outside this class in the future, maybe we should:
? |
||
|
||
private static double getPowerFactor(Load load) { | ||
return load.getP0() != 0 ? load.getQ0() / load.getP0() : 1; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is load disabling status? Why not loadsDisable ?