Skip to content

Commit

Permalink
Sensitivity analysis: clean predefined results (#618)
Browse files Browse the repository at this point in the history
Signed-off-by: Anne Tilloy <anne.tilloy@rte-france.com>
Co-authored-by: Florian Dupuy <florian.dupuy@rte-france.com>
  • Loading branch information
annetill and flo-dup authored Nov 14, 2022
1 parent e18afa5 commit ac9b798
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 196 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public class PropagatedContingency {

private final Map<String, AdmittanceShift> shuntIdsToShift;

private final Set<String> originalPowerShiftIds;

public Contingency getContingency() {
return contingency;
}
Expand Down Expand Up @@ -73,9 +75,14 @@ public Map<String, AdmittanceShift> getShuntIdsToShift() {
return shuntIdsToShift;
}

public Set<String> getOriginalPowerShiftIds() {
return originalPowerShiftIds;
}

public PropagatedContingency(Contingency contingency, int index, Set<String> branchIdsToOpen, Set<String> hvdcIdsToOpen,
Set<Switch> switchesToOpen, Set<String> generatorIdsToLose,
Map<String, PowerShift> loadIdsToShift, Map<String, AdmittanceShift> shuntIdsToShift) {
Map<String, PowerShift> loadIdsToShift, Map<String, AdmittanceShift> shuntIdsToShift,
Set<String> originalPowerShiftIds) {
this.contingency = Objects.requireNonNull(contingency);
this.index = index;
this.branchIdsToOpen = Objects.requireNonNull(branchIdsToOpen);
Expand All @@ -84,6 +91,7 @@ public PropagatedContingency(Contingency contingency, int index, Set<String> bra
this.generatorIdsToLose = Objects.requireNonNull(generatorIdsToLose);
this.loadIdsToShift = Objects.requireNonNull(loadIdsToShift);
this.shuntIdsToShift = Objects.requireNonNull(shuntIdsToShift);
this.originalPowerShiftIds = Objects.requireNonNull(originalPowerShiftIds);

for (Switch sw : switchesToOpen) {
branchIdsToOpen.add(sw.getId());
Expand Down Expand Up @@ -142,6 +150,7 @@ private static PropagatedContingency create(Network network, Contingency conting
Set<String> generatorIdsToLose = new HashSet<>();
Map<String, PowerShift> loadIdsToShift = new HashMap<>();
Map<String, AdmittanceShift> shuntIdsToShift = new HashMap<>();
Set<String> originalPowerShiftIds = new LinkedHashSet<>();

// process terminals disconnected, in particular process injection power shift
for (Terminal terminal : terminalsToDisconnect) {
Expand All @@ -159,6 +168,7 @@ private static PropagatedContingency create(Network network, Contingency conting

case LOAD:
Load load = (Load) connectable;
originalPowerShiftIds.add(load.getId());
addPowerShift(load.getTerminal(), loadIdsToShift, getLoadPowerShift(load, slackDistributionOnConformLoad), breakers);
break;

Expand All @@ -184,6 +194,7 @@ private static PropagatedContingency create(Network network, Contingency conting
LccConverterStation lcc = (LccConverterStation) connectable;
PowerShift lccPowerShift = new PowerShift(HvdcConverterStations.getConverterStationTargetP(lcc) / PerUnit.SB, 0,
HvdcConverterStations.getLccConverterStationLoadTargetQ(lcc) / PerUnit.SB);
originalPowerShiftIds.add(lcc.getId());
addPowerShift(lcc.getTerminal(), loadIdsToShift, lccPowerShift, breakers);
}
break;
Expand All @@ -205,7 +216,7 @@ private static PropagatedContingency create(Network network, Contingency conting
}

return new PropagatedContingency(contingency, index, branchIdsToOpen, hvdcIdsToOpen, switchesToOpen,
generatorIdsToLose, loadIdsToShift, shuntIdsToShift);
generatorIdsToLose, loadIdsToShift, shuntIdsToShift, originalPowerShiftIds);
}

private static void addPowerShift(Terminal terminal, Map<String, PowerShift> loadIdsToShift, PowerShift powerShift, boolean breakers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@
import com.powsybl.openloadflow.equations.EquationTerm;
import com.powsybl.openloadflow.equations.Quantity;
import com.powsybl.openloadflow.graph.GraphConnectivityFactory;
import com.powsybl.openloadflow.network.LfBranch;
import com.powsybl.openloadflow.network.LfBus;
import com.powsybl.openloadflow.network.LfElement;
import com.powsybl.openloadflow.network.LfNetwork;
import com.powsybl.openloadflow.network.*;
import com.powsybl.openloadflow.network.impl.HvdcConverterStations;
import com.powsybl.openloadflow.network.impl.LfDanglingLineBus;
import com.powsybl.openloadflow.network.impl.PropagatedContingency;
Expand Down Expand Up @@ -143,6 +140,8 @@ enum Status {

boolean isFunctionConnectedToSlackComponent(Set<LfBus> lostBuses, Set<LfBranch> lostBranches);

boolean isVariableInContingency(PropagatedContingency propagatedContingency);

SensitivityFactorGroup<V, E> getGroup();

void setGroup(SensitivityFactorGroup<V, E> group);
Expand All @@ -152,7 +151,7 @@ abstract static class AbstractLfSensitivityFactor<V extends Enum<V> & Quantity,

private final int index;

private final String variableId;
protected final String variableId;

private final String functionId;

Expand Down Expand Up @@ -357,21 +356,48 @@ public boolean isVariableConnectedToSlackComponent(Set<LfBus> disabledBuses, Set
public boolean isFunctionConnectedToSlackComponent(Set<LfBus> disabledBuses, Set<LfBranch> disabledBranches) {
return isElementConnectedToSlackComponent(functionElement, disabledBuses, disabledBranches);
}

@Override
public boolean isVariableInContingency(PropagatedContingency contingency) {
if (contingency != null) {
switch (variableType) {
case INJECTION_ACTIVE_POWER:
case HVDC_LINE_ACTIVE_POWER:
// a load, a generator, a dangling line, an LCC or a VSC converter station.
return contingency.getGeneratorIdsToLose().contains(variableId) || contingency.getOriginalPowerShiftIds().contains(variableId);
case BUS_TARGET_VOLTAGE:
// a generator or a two windings transformer.
// shunt contingency not supported yet.
// phase shifter in a three windings transformer not supported yet.
return contingency.getGeneratorIdsToLose().contains(variableId) || contingency.getBranchIdsToOpen().contains(variableId);
case TRANSFORMER_PHASE:
// a phase shifter on a two windings transformer.
return contingency.getBranchIdsToOpen().contains(variableId);
default:
return false;
}
} else {
return false;
}
}
}

static class MultiVariablesLfSensitivityFactor<V extends Enum<V> & Quantity, E extends Enum<E> & Quantity> extends AbstractLfSensitivityFactor<V, E> {

private final Map<LfElement, Double> weightedVariableElements;

private final Set<String> originalVariableSetIds;

MultiVariablesLfSensitivityFactor(int index, String variableId, String functionId,
LfElement functionElement, SensitivityFunctionType functionType,
Map<LfElement, Double> weightedVariableElements, SensitivityVariableType variableType,
ContingencyContext contingencyContext) {
ContingencyContext contingencyContext, Set<String> originalVariableSetIds) {
super(index, variableId, functionId, functionElement, functionType, variableType, contingencyContext);
this.weightedVariableElements = weightedVariableElements;
if (weightedVariableElements.isEmpty()) {
status = functionElement == null ? Status.SKIP : Status.VALID_ONLY_FOR_FUNCTION;
}
this.originalVariableSetIds = originalVariableSetIds;
}

public Map<LfElement, Double> getWeightedVariableElements() {
Expand All @@ -384,9 +410,6 @@ public Collection<LfElement> getVariableElements() {

@Override
public boolean isVariableConnectedToSlackComponent(Set<LfBus> disabledBuses, Set<LfBranch> disabledBranches) {
if (!isElementConnectedToSlackComponent(functionElement, disabledBuses, disabledBranches)) {
return false;
}
for (LfElement lfElement : getVariableElements()) {
if (isElementConnectedToSlackComponent(lfElement, disabledBuses, disabledBranches)) {
return true;
Expand All @@ -399,6 +422,19 @@ public boolean isVariableConnectedToSlackComponent(Set<LfBus> disabledBuses, Set
public boolean isFunctionConnectedToSlackComponent(Set<LfBus> disabledBuses, Set<LfBranch> disabledBranches) {
return isElementConnectedToSlackComponent(functionElement, disabledBuses, disabledBranches);
}

@Override
public boolean isVariableInContingency(PropagatedContingency contingency) {
if (contingency != null) {
int sizeCommonIds = (int) Stream.concat(contingency.getGeneratorIdsToLose().stream(), contingency.getOriginalPowerShiftIds().stream())
.distinct()
.filter(originalVariableSetIds::contains)
.count();
return sizeCommonIds == originalVariableSetIds.size();
} else {
return false;
}
}
}

interface SensitivityFactorGroup<V extends Enum<V> & Quantity, E extends Enum<E> & Quantity> {
Expand Down Expand Up @@ -623,33 +659,49 @@ protected void fillRhsSensitivityVariable(SensitivityFactorGroupList<V, E> facto
}
}

protected void setPredefinedResults(Collection<LfSensitivityFactor<V, E>> lfFactors, Set<LfBus> disabledBuses, Set<LfBranch> disabledBranches) {
protected void setPredefinedResults(Collection<LfSensitivityFactor<V, E>> lfFactors, Set<LfBus> disabledBuses,
Set<LfBranch> disabledBranches, PropagatedContingency propagatedContingency) {
for (LfSensitivityFactor<V, E> factor : lfFactors) {
if (factor.getStatus() == LfSensitivityFactor.Status.VALID) {
// after a contingency, we check if the factor function and the variable are in different connected components
boolean variableConnected = factor.isVariableConnectedToSlackComponent(disabledBuses, disabledBranches);
boolean functionConnected = factor.isFunctionConnectedToSlackComponent(disabledBuses, disabledBranches);
if (!variableConnected && functionConnected) {
// VALID_ONLY_FOR_FUNCTION status
factor.setSensitivityValuePredefinedResult(0d);
} else if (!variableConnected && !functionConnected) {
// SKIP status
factor.setSensitivityValuePredefinedResult(Double.NaN);
factor.setFunctionPredefinedResult(Double.NaN);
} else if (variableConnected && !functionConnected) {
Pair<Optional<Double>, Optional<Double>> predefinedResults = getPredefinedResults(factor, disabledBuses, disabledBranches, propagatedContingency);
predefinedResults.getLeft().ifPresent(factor::setSensitivityValuePredefinedResult);
predefinedResults.getRight().ifPresent(factor::setFunctionPredefinedResult);
}
}

protected Pair<Optional<Double>, Optional<Double>> getPredefinedResults(LfSensitivityFactor<V, E> factor, Set<LfBus> disabledBuses,
Set<LfBranch> disabledBranches, PropagatedContingency propagatedContingency) {
Double sensitivityValuePredefinedResult = null;
Double functionPredefinedResult = null;
if (factor.getStatus() == LfSensitivityFactor.Status.VALID) {
// after a contingency, we check if the factor function and the variable are in different connected components
// or if the variable is in contingency. Note that a branch in contingency is considered as not connected to the slack component.
boolean variableConnected = factor.isVariableConnectedToSlackComponent(disabledBuses, disabledBranches) && !factor.isVariableInContingency(propagatedContingency);
boolean functionConnectedToSlackComponent = factor.isFunctionConnectedToSlackComponent(disabledBuses, disabledBranches);
if (variableConnected) {
if (!functionConnectedToSlackComponent) {
// ZERO status
factor.setSensitivityValuePredefinedResult(0d);
factor.setFunctionPredefinedResult(Double.NaN);
}
} else if (factor.getStatus() == LfSensitivityFactor.Status.VALID_ONLY_FOR_FUNCTION) {
factor.setSensitivityValuePredefinedResult(0d);
if (!factor.isFunctionConnectedToSlackComponent(disabledBuses, disabledBranches)) {
factor.setFunctionPredefinedResult(Double.NaN);
sensitivityValuePredefinedResult = 0d;
functionPredefinedResult = Double.NaN;
}
} else {
throw new IllegalStateException("Unexpected factor status: " + factor.getStatus());
if (functionConnectedToSlackComponent) {
// VALID_ONLY_FOR_FUNCTION status
sensitivityValuePredefinedResult = 0d;
} else {
// SKIP status
sensitivityValuePredefinedResult = Double.NaN;
functionPredefinedResult = Double.NaN;
}
}
} else if (factor.getStatus() == LfSensitivityFactor.Status.VALID_ONLY_FOR_FUNCTION) {
sensitivityValuePredefinedResult = 0d;
if (!factor.isFunctionConnectedToSlackComponent(disabledBuses, disabledBranches)) {
functionPredefinedResult = Double.NaN;
}
} else {
throw new IllegalStateException("Unexpected factor status: " + factor.getStatus());
}
return Pair.of(Optional.ofNullable(sensitivityValuePredefinedResult), Optional.ofNullable(functionPredefinedResult));
}

protected boolean rescaleGlsk(SensitivityFactorGroupList<V, E> factorGroups, Set<LfBus> nonConnectedBuses) {
Expand Down Expand Up @@ -905,6 +957,7 @@ public SensitivityFactorHolder<V, E> readAndCheckFactors(Network network, Map<St
final SensitivityFactorHolder<V, E> factorHolder = new SensitivityFactorHolder<>();

final Map<String, Map<LfElement, Double>> injectionBusesByVariableId = new LinkedHashMap<>();
final Map<String, Set<String>> originalVariableSetIdsByVariableId = new LinkedHashMap<>();
final Map<String, Bus> busCache = new HashMap<>();
int[] factorIndex = new int[1];
factorReader.read((functionType, functionId, variableType, variableId, variableSet, contingencyContext) -> {
Expand All @@ -917,9 +970,12 @@ public SensitivityFactorHolder<V, E> readAndCheckFactors(Network network, Map<St
LfElement functionElement = branch != null && branch.getBus1() != null && branch.getBus2() != null ? branch : null;
if (variableType == SensitivityVariableType.INJECTION_ACTIVE_POWER) {
Map<LfElement, Double> injectionLfBuses = injectionBusesByVariableId.get(variableId);
if (injectionLfBuses == null) {
Set<String> originalVariableSetIds = originalVariableSetIdsByVariableId.get(variableId);
if (injectionLfBuses == null && originalVariableSetIds == null) {
injectionLfBuses = new LinkedHashMap<>();
originalVariableSetIds = new HashSet<>();
injectionBusesByVariableId.put(variableId, injectionLfBuses);
originalVariableSetIdsByVariableId.put(variableId, originalVariableSetIds);
SensitivityVariableSet set = variableSetsById.get(variableId);
if (set == null) {
throw new PowsyblException("Variable set '" + variableId + "' not found");
Expand All @@ -933,14 +989,15 @@ public SensitivityFactorHolder<V, E> readAndCheckFactors(Network network, Map<St
continue;
}
injectionLfBuses.put(injectionLfBus, injectionLfBuses.getOrDefault(injectionLfBus, 0d) + variable.getWeight());
originalVariableSetIds.add(variable.getId());
}
if (!skippedInjection.isEmpty() && LOGGER.isWarnEnabled()) {
LOGGER.warn("Injections {} cannot be found for glsk {} and will be ignored", String.join(", ", skippedInjection), variableId);
}
}
factorHolder.addFactor(new MultiVariablesLfSensitivityFactor<>(factorIndex[0], variableId,
functionId, functionElement, functionType,
injectionLfBuses, variableType, contingencyContext));
injectionLfBuses, variableType, contingencyContext, originalVariableSetIds));
} else {
throw createVariableTypeNotSupportedWithFunctionTypeException(variableType, functionType);
}
Expand Down Expand Up @@ -968,17 +1025,20 @@ public SensitivityFactorHolder<V, E> readAndCheckFactors(Network network, Map<St
// corresponds to an augmentation of +1 on the active power setpoint on each side on the HVDC line
// => we create a multi (bi) variables factor
Map<LfElement, Double> injectionLfBuses = new HashMap<>(2);
Set<String> originalVariableSetIds = new HashSet<>(2);
if (bus1 != null) {
// FIXME: for LCC, Q changes when P changes
injectionLfBuses.put(bus1, HvdcConverterStations.getActivePowerSetpointMultiplier(hvdcLine.getConverterStation1()));
originalVariableSetIds.add(hvdcLine.getConverterStation1().getId());
}
if (bus2 != null) {
// FIXME: for LCC, Q changes when P changes
injectionLfBuses.put(bus2, HvdcConverterStations.getActivePowerSetpointMultiplier(hvdcLine.getConverterStation2()));
originalVariableSetIds.add(hvdcLine.getConverterStation2().getId());
}

factorHolder.addFactor(new MultiVariablesLfSensitivityFactor<>(factorIndex[0], variableId,
functionId, functionElement, functionType, injectionLfBuses, variableType, contingencyContext));
functionId, functionElement, functionType, injectionLfBuses, variableType, contingencyContext, originalVariableSetIds));
} else {
LfElement functionElement;
LfElement variableElement;
Expand Down
Loading

0 comments on commit ac9b798

Please sign in to comment.