Skip to content

Commit

Permalink
Try to fix AC sensitivity for no impact contingency after normal cont…
Browse files Browse the repository at this point in the history
…ingency (#631)

Signed-off-by: Anne Tilloy <[email protected]>
Signed-off-by: Geoffroy Jamgotchian <[email protected]>
  • Loading branch information
annetill authored Oct 19, 2022
1 parent 2e8528d commit 34eabfa
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ public void checkContingencies(LfNetwork lfNetwork, List<PropagatedContingency>
&& contingency.getHvdcIdsToOpen().isEmpty()
&& contingency.getGeneratorIdsToLose().isEmpty()
&& contingency.getLoadIdsToShift().isEmpty()) {
LOGGER.warn("Contingency {} has no impact", contingency.getContingency().getId());
LOGGER.warn("Contingency '{}' has no impact", contingency.getContingency().getId());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.powsybl.openloadflow.OpenLoadFlowParameters;
import com.powsybl.openloadflow.ac.equations.AcEquationType;
import com.powsybl.openloadflow.ac.equations.AcVariableType;
import com.powsybl.openloadflow.ac.nr.NewtonRaphson;
import com.powsybl.openloadflow.ac.nr.NewtonRaphsonStatus;
import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowContext;
import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters;
Expand Down Expand Up @@ -117,13 +118,14 @@ private void calculatePostContingencySensitivityValues(List<LfSensitivityFactor<
activePowerDistribution.run(lfNetwork, lfContingency.getActivePowerLoss());
}

context.getParameters().setVoltageInitializer(new PreviousValueVoltageInitializer());
AcLoadFlowResult result = new AcloadFlowEngine(context)
.run();
if (!runLoadFlow(context, false)) {
// write contingency status
resultWriter.writeContingencyStatus(contingencyIndex, SensitivityAnalysisResult.Status.FAILURE);
return;
}

// write contingency status
resultWriter.writeContingencyStatus(contingencyIndex, (result.getNewtonRaphsonStatus() == NewtonRaphsonStatus.CONVERGED) ?
SensitivityAnalysisResult.Status.SUCCESS : SensitivityAnalysisResult.Status.FAILURE);
resultWriter.writeContingencyStatus(contingencyIndex, SensitivityAnalysisResult.Status.SUCCESS);

// if we have at least one bus target voltage linked to a ratio tap changer, we have to rebuild the AC equation
// system obtained just before the transformer steps rounding.
Expand Down Expand Up @@ -162,6 +164,21 @@ public void checkContingencies(LfNetwork lfNetwork, List<PropagatedContingency>
}
}

private static boolean runLoadFlow(AcLoadFlowContext context, boolean throwsExceptionIfNoConvergence) {
AcLoadFlowResult result = new AcloadFlowEngine(context)
.run();
if (result.getNewtonRaphsonStatus() != NewtonRaphsonStatus.CONVERGED
&& result.getNewtonRaphsonStatus() != NewtonRaphsonStatus.NO_CALCULATION) {
if (throwsExceptionIfNoConvergence) {
throw new PowsyblException("Loadflow diverged with status " + result.getNewtonRaphsonStatus());
} else {
LOGGER.warn("Loadflow diverged with status {}", result.getNewtonRaphsonStatus());
return false;
}
}
return true;
}

/**
* https://people.montefiore.uliege.be/vct/elec0029/lf.pdf / Equation 32 is transposed
*/
Expand Down Expand Up @@ -235,8 +252,7 @@ public void analyse(Network network, List<PropagatedContingency> contingencies,

try (AcLoadFlowContext context = new AcLoadFlowContext(lfNetwork, acParameters)) {

new AcloadFlowEngine(context)
.run();
runLoadFlow(context, true);

// index factors by variable group to compute a minimal number of states
SensitivityFactorGroupList<AcVariableType, AcEquationType> factorGroups = createFactorGroups(validLfFactors.stream()
Expand Down Expand Up @@ -286,65 +302,74 @@ public void analyse(Network network, List<PropagatedContingency> contingencies,

NetworkState networkState = NetworkState.save(lfNetwork);

contingencies.forEach(contingency -> contingency.toLfContingency(lfNetwork)
.ifPresentOrElse(lfContingency -> {
List<LfSensitivityFactor<AcVariableType, AcEquationType>> contingencyFactors = validFactorHolder.getFactorsForContingency(lfContingency.getId());
contingencyFactors.forEach(lfFactor -> {
lfFactor.setSensitivityValuePredefinedResult(null);
lfFactor.setFunctionPredefinedResult(null);
});

lfContingency.apply(lfParameters.getBalanceType());

Map<LfBus, Double> postContingencySlackParticipationByBus;
Set<LfBus> slackConnectedComponent;
if (lfContingency.getDisabledBuses().isEmpty()) {
// contingency not breaking connectivity
LOGGER.debug("Contingency {} without loss of connectivity", lfContingency.getId());
slackConnectedComponent = new HashSet<>(lfNetwork.getBuses());

// Sensitivity values 0 and function reference NaN in case of a sensitivity on a disabled branch
contingencyFactors.stream()
.filter(lfFactor -> lfFactor.getFunctionElement() instanceof LfBranch)
.filter(lfFactor -> lfContingency.getDisabledBranches().contains(lfFactor.getFunctionElement()))
.forEach(lfFactor -> {
lfFactor.setSensitivityValuePredefinedResult(0d);
lfFactor.setFunctionPredefinedResult(Double.NaN);
});

// Sensitivity values 0 in case of a sensitivity from the transformer phase of a disabled transformer
contingencyFactors.stream()
.filter(lfFactor -> lfFactor.getVariableType().equals(SensitivityVariableType.TRANSFORMER_PHASE))
.filter(lfFactor -> lfContingency.getDisabledBranches().contains(lfNetwork.getBranchById(lfFactor.getVariableId())))
.forEach(lfFactor -> lfFactor.setSensitivityValuePredefinedResult(0d));
} else {
// contingency breaking connectivity
LOGGER.debug("Contingency {} with loss of connectivity", lfContingency.getId());
// we check if factors are still in the main component
slackConnectedComponent = new HashSet<>(lfNetwork.getBuses()).stream().filter(Predicate.not(lfContingency.getDisabledBuses()::contains)).collect(Collectors.toSet());
setPredefinedResults(contingencyFactors, lfContingency.getDisabledBuses(), lfContingency.getDisabledBranches());
// we recompute GLSK weights if needed
rescaleGlsk(factorGroups, lfContingency.getDisabledBuses());
}

// compute the participation for each injection factor (+1 on the injection and then -participation factor on all
// buses that contain elements participating to slack distribution)
if (lfParameters.isDistributedSlack()) {
postContingencySlackParticipationByBus = getParticipatingElements(slackConnectedComponent, lfParameters.getBalanceType(), lfParametersExt).stream().collect(Collectors.toMap(
ParticipatingElement::getLfBus, element -> -element.getFactor(), Double::sum));
} else {
postContingencySlackParticipationByBus = Collections.singletonMap(lfNetwork.getSlackBus(), -1d);
}
calculatePostContingencySensitivityValues(contingencyFactors, lfContingency, lfNetwork, context, factorGroups, postContingencySlackParticipationByBus,
lfParameters, lfParametersExt, lfContingency.getIndex(), resultWriter, Boolean.TRUE.equals(hasBusTargetVoltage.getRight()));

networkState.restore();
}, () -> {
// It means that the contingency has no impact.
calculateSensitivityValues(validFactorHolder.getFactorsForContingency(contingency.getContingency().getId()), factorGroups, factorsStates, contingency.getIndex(), resultWriter);
// write contingency status
resultWriter.writeContingencyStatus(contingency.getIndex(), SensitivityAnalysisResult.Status.NO_IMPACT);
}));
// we always restart from base case voltages for contingency simulation
context.getParameters().setVoltageInitializer(new PreviousValueVoltageInitializer());

contingencies.forEach(contingency -> {
LOGGER.info("Simulate contingency '{}'", contingency.getContingency().getId());
contingency.toLfContingency(lfNetwork)
.ifPresentOrElse(lfContingency -> {
List<LfSensitivityFactor<AcVariableType, AcEquationType>> contingencyFactors = validFactorHolder.getFactorsForContingency(lfContingency.getId());
contingencyFactors.forEach(lfFactor -> {
lfFactor.setSensitivityValuePredefinedResult(null);
lfFactor.setFunctionPredefinedResult(null);
});

lfContingency.apply(lfParameters.getBalanceType());

Map<LfBus, Double> postContingencySlackParticipationByBus;
Set<LfBus> slackConnectedComponent;
if (lfContingency.getDisabledBuses().isEmpty()) {
// contingency not breaking connectivity
LOGGER.debug("Contingency '{}' without loss of connectivity", lfContingency.getId());
slackConnectedComponent = new HashSet<>(lfNetwork.getBuses());

// Sensitivity values 0 and function reference NaN in case of a sensitivity on a disabled branch
contingencyFactors.stream()
.filter(lfFactor -> lfFactor.getFunctionElement() instanceof LfBranch)
.filter(lfFactor -> lfContingency.getDisabledBranches().contains(lfFactor.getFunctionElement()))
.forEach(lfFactor -> {
lfFactor.setSensitivityValuePredefinedResult(0d);
lfFactor.setFunctionPredefinedResult(Double.NaN);
});

// Sensitivity values 0 in case of a sensitivity from the transformer phase of a disabled transformer
contingencyFactors.stream()
.filter(lfFactor -> lfFactor.getVariableType().equals(SensitivityVariableType.TRANSFORMER_PHASE))
.filter(lfFactor -> lfContingency.getDisabledBranches().contains(lfNetwork.getBranchById(lfFactor.getVariableId())))
.forEach(lfFactor -> lfFactor.setSensitivityValuePredefinedResult(0d));
} else {
// contingency breaking connectivity
LOGGER.debug("Contingency {} with loss of connectivity", lfContingency.getId());
// we check if factors are still in the main component
slackConnectedComponent = new HashSet<>(lfNetwork.getBuses()).stream().filter(Predicate.not(lfContingency.getDisabledBuses()::contains)).collect(Collectors.toSet());
setPredefinedResults(contingencyFactors, lfContingency.getDisabledBuses(), lfContingency.getDisabledBranches());
// we recompute GLSK weights if needed
rescaleGlsk(factorGroups, lfContingency.getDisabledBuses());
}

// compute the participation for each injection factor (+1 on the injection and then -participation factor on all
// buses that contain elements participating to slack distribution)
if (lfParameters.isDistributedSlack()) {
postContingencySlackParticipationByBus = getParticipatingElements(slackConnectedComponent, lfParameters.getBalanceType(), lfParametersExt).stream().collect(Collectors.toMap(
ParticipatingElement::getLfBus, element -> -element.getFactor(), Double::sum));
} else {
postContingencySlackParticipationByBus = Collections.singletonMap(lfNetwork.getSlackBus(), -1d);
}
calculatePostContingencySensitivityValues(contingencyFactors, lfContingency, lfNetwork, context, factorGroups, postContingencySlackParticipationByBus,
lfParameters, lfParametersExt, lfContingency.getIndex(), resultWriter, Boolean.TRUE.equals(hasBusTargetVoltage.getRight()));

networkState.restore();
}, () -> {
// it means that the contingency has no impact.
// we need to force the state vector to be re-initialized from base case network state
NewtonRaphson.initStateVector(lfNetwork, context.getEquationSystem(), context.getParameters().getVoltageInitializer());

calculateSensitivityValues(validFactorHolder.getFactorsForContingency(contingency.getContingency().getId()), factorGroups, factorsStates, contingency.getIndex(), resultWriter);
// write contingency status
resultWriter.writeContingencyStatus(contingency.getIndex(), SensitivityAnalysisResult.Status.NO_IMPACT);
});
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1087,4 +1087,42 @@ void testSwitchContingency2() {
assertTrue(e.getCause() instanceof PowsyblException);
assertEquals("Switch contingency is not yet supported with sensitivity function of type BUS_VOLTAGE", e.getCause().getMessage());
}

@Test
void testNoImpactContingencyAfterNormalContingency() {
Network network = ConnectedComponentNetworkFactory.createTwoCcLinkedByTwoLines();
// we open l45 at both sides
Line l13 = network.getLine("l13");
l13.getTerminal1().disconnect();
l13.getTerminal2().disconnect();

SensitivityAnalysisParameters sensiParameters = createParameters(false, "b1_vl_0", true);
sensiParameters.getLoadFlowParameters().setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD);

List<Contingency> contingencies = List.of(new Contingency("lines", List.of(new BranchContingency("l46"), new BranchContingency("l56"))),
new Contingency("l13", new BranchContingency("l13")));

ContingencyContext contingencyContext = new ContingencyContext("l13", ContingencyContextType.SPECIFIC);
SensitivityFactor factor = new SensitivityFactor(SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, "l46",
SensitivityVariableType.INJECTION_ACTIVE_POWER,
"d1", false,
contingencyContext);
List<SensitivityFactor> factors = List.of(factor);

SensitivityAnalysisResult result = sensiRunner.run(network, factors, contingencies, Collections.emptyList(), sensiParameters);
assertEquals(0.0735, result.getBranchFlow1SensitivityValue("l13", "d1", "l46"), LoadFlowAssert.DELTA_POWER);
assertEquals(SensitivityAnalysisResult.Status.NO_IMPACT, result.getContingencyStatus("l13"));
}

@Test
void testMaxIterationReachedAfterContingency() {
Network network = EurostagTutorialExample1Factory.create();
network.getLine("NHV1_NHV2_1").setX(1000);
List<Contingency> contingencies = List.of(new Contingency("NHV1_NHV2_2", List.of(new BranchContingency("NHV1_NHV2_2"))));
List<SensitivityFactor> factors = List.of(createBranchFlowPerInjectionIncrease("NHV1_NHV2_1", "LOAD"));
SensitivityAnalysisParameters parameters = new SensitivityAnalysisParameters();
parameters.getLoadFlowParameters().setDistributedSlack(false);
SensitivityAnalysisResult result = sensiRunner.run(network, factors, contingencies, Collections.emptyList(), parameters);
assertEquals(SensitivityAnalysisResult.Status.FAILURE, result.getContingencyStatus("NHV1_NHV2_2"));
}
}

0 comments on commit 34eabfa

Please sign in to comment.