diff --git a/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemUpdater.java b/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemUpdater.java index 5158aeb8ae..72d3ea70d6 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemUpdater.java +++ b/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemUpdater.java @@ -113,6 +113,9 @@ public void onDisableChange(LfElement element, boolean disabled) { LfShunt shunt = (LfShunt) element; shunt.getVoltageControl().ifPresent(voltageControl -> AcEquationSystem.updateShuntVoltageControlEquations(voltageControl, equationSystem)); break; + case HVDC: + // nothing to do + break; default: throw new IllegalStateException("Unknown element type: " + element.getType()); } diff --git a/src/main/java/com/powsybl/openloadflow/network/LfContingency.java b/src/main/java/com/powsybl/openloadflow/network/LfContingency.java index 0efec96012..6683fa7149 100644 --- a/src/main/java/com/powsybl/openloadflow/network/LfContingency.java +++ b/src/main/java/com/powsybl/openloadflow/network/LfContingency.java @@ -14,10 +14,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.io.Writer; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; /** * @author Geoffroy Jamgotchian @@ -32,30 +29,33 @@ public class LfContingency { private final Set disabledBranches; + private final Set disabledHvdcs; + private final Map shuntsShift; private final Map busesLoadShift; - private final Set generators; + private final Set lostGenerators; private double activePowerLoss = 0; public LfContingency(String id, int index, Set disabledBuses, Set disabledBranches, Map shuntsShift, - Map busesLoadShift, Set generators) { + Map busesLoadShift, Set lostGenerators, Set disabledHvdcs) { this.id = Objects.requireNonNull(id); this.index = index; this.disabledBuses = Objects.requireNonNull(disabledBuses); this.disabledBranches = Objects.requireNonNull(disabledBranches); + this.disabledHvdcs = Objects.requireNonNull(disabledHvdcs); this.shuntsShift = Objects.requireNonNull(shuntsShift); this.busesLoadShift = Objects.requireNonNull(busesLoadShift); - this.generators = Objects.requireNonNull(generators); + this.lostGenerators = Objects.requireNonNull(lostGenerators); for (LfBus bus : disabledBuses) { activePowerLoss += bus.getGenerationTargetP() - bus.getLoadTargetP(); } for (Map.Entry e : busesLoadShift.entrySet()) { activePowerLoss -= e.getValue().getActive(); } - for (LfGenerator generator : generators) { + for (LfGenerator generator : lostGenerators) { activePowerLoss += generator.getTargetP(); } } @@ -84,18 +84,21 @@ public Map getBusesLoadShift() { return busesLoadShift; } - public Set getGenerators() { - return generators; + public Set getLostGenerators() { + return lostGenerators; } public double getActivePowerLoss() { return activePowerLoss; } - public void apply(LoadFlowParameters parameters) { + public void apply(LoadFlowParameters.BalanceType balanceType) { for (LfBranch branch : disabledBranches) { branch.setDisabled(true); } + for (LfHvdc hvdc : disabledHvdcs) { + hvdc.setDisabled(true); + } for (LfBus bus : disabledBuses) { bus.setDisabled(true); } @@ -106,12 +109,12 @@ public void apply(LoadFlowParameters parameters) { for (var e : busesLoadShift.entrySet()) { LfBus bus = e.getKey(); PowerShift shift = e.getValue(); - bus.setLoadTargetP(bus.getLoadTargetP() - getUpdatedLoadP0(bus, parameters.getBalanceType(), shift.getActive(), shift.getVariableActive())); + bus.setLoadTargetP(bus.getLoadTargetP() - getUpdatedLoadP0(bus, balanceType, shift.getActive(), shift.getVariableActive())); bus.setLoadTargetQ(bus.getLoadTargetQ() - shift.getReactive()); bus.getLoads().setAbsVariableLoadTargetP(bus.getLoads().getAbsVariableLoadTargetP() - Math.abs(shift.getVariableActive()) * PerUnit.SB); } Set generatorBuses = new HashSet<>(); - for (LfGenerator generator : generators) { + for (LfGenerator generator : lostGenerators) { generator.setTargetP(0); LfBus bus = generator.getBus(); generatorBuses.add(bus); @@ -131,14 +134,33 @@ public void apply(LoadFlowParameters parameters) { public static double getUpdatedLoadP0(LfBus bus, LoadFlowParameters.BalanceType balanceType, double initialP0, double initialVariableActivePower) { double factor = 0.0; - if (balanceType == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD) { - factor = Math.abs(initialP0) / (bus.getLoads().getAbsVariableLoadTargetP() / PerUnit.SB); - } else if (balanceType == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD) { - factor = initialVariableActivePower / (bus.getLoads().getAbsVariableLoadTargetP() / PerUnit.SB); + if (bus.getLoads().getLoadCount() > 0) { + if (balanceType == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD) { + factor = Math.abs(initialP0) / (bus.getLoads().getAbsVariableLoadTargetP() / PerUnit.SB); + } else if (balanceType == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD) { + factor = initialVariableActivePower / (bus.getLoads().getAbsVariableLoadTargetP() / PerUnit.SB); + } } return initialP0 + (bus.getLoadTargetP() - bus.getInitialLoadTargetP()) * factor; } + public Set getLoadAndGeneratorBuses() { + Set buses = new HashSet<>(); + for (var e : busesLoadShift.entrySet()) { + LfBus bus = e.getKey(); + if (bus != null) { + buses.add(bus); + } + } + for (LfGenerator generator : lostGenerators) { + LfBus bus = generator.getBus(); + if (bus != null) { + buses.add(bus); + } + } + return buses; + } + public void writeJson(Writer writer) { Objects.requireNonNull(writer); try (JsonGenerator jsonGenerator = new JsonFactory() diff --git a/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java b/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java index 67c0c695bf..ba1c64e32d 100644 --- a/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java +++ b/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java @@ -70,6 +70,8 @@ public class LfNetwork extends AbstractPropertyBag implements PropertyBag { private final List hvdcs = new ArrayList<>(); + private final Map hvdcsById = new HashMap<>(); + private final List listeners = new ArrayList<>(); private boolean valid = true; @@ -198,6 +200,7 @@ public void addHvdc(LfHvdc hvdc) { Objects.requireNonNull(hvdc); hvdc.setNum(hvdcs.size()); hvdcs.add(hvdc); + hvdcsById.put(hvdc.getId(), hvdc); // create bus -> branches link if (hvdc.getBus1() != null) { @@ -212,6 +215,11 @@ public List getHvdcs() { return hvdcs; } + public LfHvdc getHvdcById(String id) { + Objects.requireNonNull(id); + return hvdcsById.get(id); + } + public void updateState(boolean reactiveLimits, boolean writeSlackBus, boolean phaseShifterRegulationOn, boolean transformerVoltageControlOn, boolean distributedOnConformLoad, boolean loadPowerFactorConstant, boolean dc) { diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/PropagatedContingency.java b/src/main/java/com/powsybl/openloadflow/network/impl/PropagatedContingency.java index c142323ce1..6476c6f55f 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/PropagatedContingency.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/PropagatedContingency.java @@ -10,6 +10,7 @@ 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.openloadflow.graph.GraphDecrementalConnectivity; import com.powsybl.openloadflow.network.*; @@ -36,7 +37,7 @@ public class PropagatedContingency { private final Set switchesToOpen; - private final Set hvdcIdsToOpen; + private final Set hvdcIdsToOpen; // for HVDC in AC emulation private final Set generatorIdsToLose; @@ -108,11 +109,11 @@ private static PowerShift getLoadPowerShift(Load load, boolean slackDistribution } public static List createListForSensitivityAnalysis(Network network, List contingencies, - boolean slackDistributionOnConformLoad) { + boolean slackDistributionOnConformLoad, boolean hvdcAcEmulation) { List propagatedContingencies = new ArrayList<>(); for (int index = 0; index < contingencies.size(); index++) { Contingency contingency = contingencies.get(index); - PropagatedContingency propagatedContingency = PropagatedContingency.create(network, contingency, index, false, false, slackDistributionOnConformLoad); + PropagatedContingency propagatedContingency = PropagatedContingency.create(network, contingency, index, false, false, slackDistributionOnConformLoad, hvdcAcEmulation); Optional coupler = propagatedContingency.switchesToOpen.stream().filter(PropagatedContingency::isCoupler).findFirst(); if (coupler.isEmpty()) { propagatedContingencies.add(propagatedContingency); @@ -127,12 +128,12 @@ public static List createListForSensitivityAnalysis(Netwo public static List createListForSecurityAnalysis(Network network, List contingencies, Set allSwitchesToOpen, boolean shuntCompensatorVoltageControlOn, - boolean slackDistributionOnConformLoad) { + boolean slackDistributionOnConformLoad, boolean hvdcAcEmulation) { List propagatedContingencies = new ArrayList<>(); for (int index = 0; index < contingencies.size(); index++) { Contingency contingency = contingencies.get(index); PropagatedContingency propagatedContingency = - PropagatedContingency.create(network, contingency, index, shuntCompensatorVoltageControlOn, true, slackDistributionOnConformLoad); + PropagatedContingency.create(network, contingency, index, shuntCompensatorVoltageControlOn, true, slackDistributionOnConformLoad, hvdcAcEmulation); propagatedContingencies.add(propagatedContingency); allSwitchesToOpen.addAll(propagatedContingency.switchesToOpen); } @@ -140,11 +141,13 @@ public static List createListForSecurityAnalysis(Network } private static PropagatedContingency create(Network network, Contingency contingency, int index, boolean shuntCompensatorVoltageControlOn, - boolean withBreakers, boolean slackDistributionOnConformLoad) { + boolean withBreakers, boolean slackDistributionOnConformLoad, boolean hvdcAcEmulation) { Set switchesToOpen = new HashSet<>(); Set terminalsToDisconnect = new HashSet<>(); Set branchIdsToOpen = new LinkedHashSet<>(); Set hvdcIdsToOpen = new HashSet<>(); + Set vscsToLose = new HashSet<>(); + Set lccsToLose = new HashSet<>(); Set loadsToLose = new HashSet<>(); Set generatorsToLose = new HashSet<>(); Set shuntsToLose = new HashSet<>(); @@ -164,7 +167,24 @@ private static PropagatedContingency create(Network network, Contingency conting if (hvdcLine == null) { throw new PowsyblException("HVDC line '" + element.getId() + "' not found in the network"); } - hvdcIdsToOpen.add(element.getId()); + HvdcAngleDroopActivePowerControl control = hvdcLine.getExtension(HvdcAngleDroopActivePowerControl.class); + if (control != null && control.isEnabled() && hvdcAcEmulation) { + hvdcIdsToOpen.add(element.getId()); + } + if (hvdcLine.getConverterStation1() instanceof VscConverterStation) { + VscConverterStation vsc1 = (VscConverterStation) hvdcLine.getConverterStation1(); + vscsToLose.add(vsc1); + } else { + LccConverterStation lcc1 = (LccConverterStation) hvdcLine.getConverterStation1(); + lccsToLose.add(lcc1); + } + if (hvdcLine.getConverterStation2() instanceof VscConverterStation) { + VscConverterStation vsc2 = (VscConverterStation) hvdcLine.getConverterStation2(); + vscsToLose.add(vsc2); + } else { + LccConverterStation lcc2 = (LccConverterStation) hvdcLine.getConverterStation2(); + lccsToLose.add(lcc2); + } break; case DANGLING_LINE: // dangling line check is done inside tripping @@ -229,6 +249,20 @@ private static PropagatedContingency create(Network network, Contingency conting shuntsToLose.add((ShuntCompensator) connectable); break; + case HVDC_CONVERTER_STATION: + HvdcConverterStation station = (HvdcConverterStation) connectable; + HvdcAngleDroopActivePowerControl control = station.getHvdcLine().getExtension(HvdcAngleDroopActivePowerControl.class); + if (control != null && control.isEnabled() && hvdcAcEmulation) { + hvdcIdsToOpen.add(station.getHvdcLine().getId()); + } else { + if (connectable instanceof VscConverterStation) { + vscsToLose.add((VscConverterStation) connectable); + } else { + lccsToLose.add((LccConverterStation) connectable); + } + } + break; + case BUSBAR_SECTION: // we don't care break; @@ -246,6 +280,9 @@ private static PropagatedContingency create(Network network, Contingency conting for (Generator generator : generatorsToLose) { generatorIdsToLose.add(generator.getId()); } + for (VscConverterStation vsc : vscsToLose) { + generatorIdsToLose.add(vsc.getId()); + } for (Load load : loadsToLose) { Bus bus = withBreakers ? load.getTerminal().getBusBreakerView().getBus() : load.getTerminal().getBusView().getBus(); @@ -254,6 +291,16 @@ private static PropagatedContingency create(Network network, Contingency conting .add(getLoadPowerShift(load, slackDistributionOnConformLoad)); } } + for (LccConverterStation lcc : lccsToLose) { + Bus bus = withBreakers ? lcc.getTerminal().getBusBreakerView().getBus() + : lcc.getTerminal().getBusView().getBus(); + if (bus != null) { + // No variable active part for a LCC. + loadIdsToShift.computeIfAbsent(bus.getId(), k -> new PowerShift()) + .add(new PowerShift(HvdcConverterStations.getConverterStationTargetP(lcc) / PerUnit.SB, 0, + HvdcConverterStations.getLccConverterStationLoadTargetQ(lcc) / PerUnit.SB)); + } + } for (ShuntCompensator shunt : shuntsToLose) { double nominalV = shunt.getTerminal().getVoltageLevel().getNominalV(); shuntIdsToShift.put(shunt.getId(), shunt.getB() * nominalV * nominalV / PerUnit.SB); @@ -302,10 +349,6 @@ public Optional toLfContingency(LfNetwork network, boolean useSma // reset connectivity to discard triggered branches connectivity.reset(); - if (!hvdcIdsToOpen.isEmpty()) { - throw new UnsupportedOperationException("HVDC line contingency not supported"); - } - Map shunts = new HashMap<>(1); for (var e : shuntIdsToShift.entrySet()) { LfShunt shunt = network.getShuntById(e.getKey()); @@ -333,14 +376,21 @@ public Optional toLfContingency(LfNetwork network, boolean useSma } } + // find hvdc lines that are part of this network + Set hvdcs = hvdcIdsToOpen.stream() + .map(network::getHvdcById) + .filter(Objects::nonNull) // could be in another component + .collect(Collectors.toSet()); + if (branches.isEmpty() && buses.isEmpty() && shunts.isEmpty() && busesLoadShift.isEmpty() - && generators.isEmpty()) { + && generators.isEmpty() + && hvdcs.isEmpty()) { return Optional.empty(); } - return Optional.of(new LfContingency(contingency.getId(), index, buses, branches, shunts, busesLoadShift, generators)); + return Optional.of(new LfContingency(contingency.getId(), index, buses, branches, shunts, busesLoadShift, generators, hvdcs)); } } diff --git a/src/main/java/com/powsybl/openloadflow/sa/AcSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/AcSecurityAnalysis.java index a768ca357d..c3b46f9d72 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/AcSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/AcSecurityAnalysis.java @@ -68,7 +68,8 @@ SecurityAnalysisReport runSync(String workingVariantId, SecurityAnalysisParamete // try to find all switches impacted by at least one contingency and for each contingency the branches impacted Set allSwitchesToOpen = new HashSet<>(); List propagatedContingencies = PropagatedContingency.createListForSecurityAnalysis(network, contingencies, allSwitchesToOpen, - lfParameters.isShuntCompensatorVoltageControlOn(), lfParameters.getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD); + lfParameters.isShuntCompensatorVoltageControlOn(), lfParameters.getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD, + lfParameters.isHvdcAcEmulation()); AcLoadFlowParameters acParameters = OpenLoadFlowParameters.createAcParameters(network, lfParameters, lfParametersExt, matrixFactory, connectivityFactory, Reporter.NO_OP, true, false); @@ -144,7 +145,7 @@ private SecurityAnalysisResult runSimulations(LfNetwork network, List { // only process contingencies that impact the network - lfContingency.apply(loadFlowParameters); + lfContingency.apply(loadFlowParameters.getBalanceType()); distributedMismatch(network, lfContingency.getActivePowerLoss(), loadFlowParameters, openLoadFlowParameters); @@ -171,7 +172,7 @@ private PostContingencyResult runPostContingencySimulation(LfNetwork network, Ac Map preContingencyBranchResults, SecurityAnalysisParameters.IncreasedViolationsParameters violationsParameters) { LOGGER.info("Start post contingency '{}' simulation on network {}", lfContingency.getId(), network); LOGGER.debug("Contingency '{}' impact on network {}: remove {} buses, remove {} branches, remove {} generators, shift {} shunts, shift load of {} buses", - lfContingency.getId(), network, lfContingency.getDisabledBuses(), lfContingency.getDisabledBranches(), lfContingency.getGenerators(), + lfContingency.getId(), network, lfContingency.getDisabledBuses(), lfContingency.getDisabledBranches(), lfContingency.getLostGenerators(), lfContingency.getShuntsShift(), lfContingency.getBusesLoadShift()); Stopwatch stopwatch = Stopwatch.createStarted(); diff --git a/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java index c9a3bed906..5b5cc440f9 100644 --- a/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java @@ -21,10 +21,7 @@ import com.powsybl.openloadflow.equations.Quantity; import com.powsybl.openloadflow.graph.GraphDecrementalConnectivity; import com.powsybl.openloadflow.graph.GraphDecrementalConnectivityFactory; -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; diff --git a/src/main/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysis.java index 1b9563fc07..815dd5421c 100644 --- a/src/main/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysis.java @@ -110,14 +110,7 @@ private void calculatePostContingencySensitivityValues(List participationByBus, LoadFlowParameters lfParameters, OpenLoadFlowParameters lfParametersExt, int contingencyIndex, SensitivityValueWriter valueWriter, - Reporter reporter, boolean hasTransformerBusTargetVoltage) { - for (LfBranch branch : lfContingency.getDisabledBranches()) { - branch.setDisabled(true); - } - for (LfBus bus : lfContingency.getDisabledBuses()) { - bus.setDisabled(true); - } - + Reporter reporter, boolean hasTransformerBusTargetVoltage, boolean hasMultiVariables) { if (lfParameters.isDistributedSlack() && Math.abs(lfContingency.getActivePowerLoss()) > 0) { ActivePowerDistribution activePowerDistribution = ActivePowerDistribution.create(lfParameters.getBalanceType(), lfParametersExt.isLoadPowerFactorConstant()); activePowerDistribution.run(lfNetwork, lfContingency.getActivePowerLoss()); @@ -136,6 +129,12 @@ private void calculatePostContingencySensitivityValues(List affectedBuses = lfContingency.getLoadAndGeneratorBuses(); + rescaleGlsk(factorGroups, affectedBuses); + } + // we make the assumption that we ran a loadflow before, and thus this jacobian is the right one // solve system @@ -152,8 +151,8 @@ public void checkContingencies(LfNetwork lfNetwork, List super.checkContingencies(lfNetwork, contingencies); for (PropagatedContingency contingency : contingencies) { - if (!contingency.getHvdcIdsToOpen().isEmpty()) { - throw new NotImplementedException("Contingencies on a DC line are not yet supported in AC mode."); + if (!contingency.getShuntIdsToShift().isEmpty()) { + throw new NotImplementedException("Shunt Contingencies are not yet supported in AC mode."); } } } @@ -224,6 +223,8 @@ public void analyse(Network network, List contingencies, List> factorGroups = createFactorGroups(validLfFactors.stream() .filter(factor -> factor.getStatus() == LfSensitivityFactor.Status.VALID).collect(Collectors.toList())); + boolean hasMultiVariables = factorGroups.stream().anyMatch(MultiVariablesFactorGroup.class::isInstance); + // 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 @@ -276,6 +277,9 @@ public void analyse(Network network, List contingencies, lfFactor.setFunctionPredefinedResult(null); }); + lfContingency.apply(lfParameters.getBalanceType()); + + // 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())) @@ -283,40 +287,38 @@ public void analyse(Network network, List contingencies, 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)); Map postContingencySlackParticipationByBus; + Set slackConnectedComponent; if (lfContingency.getDisabledBuses().isEmpty()) { // contingency not breaking connectivity - postContingencySlackParticipationByBus = slackParticipationByBus; + LOGGER.debug("Contingency {} without loss of connectivity", lfContingency.getId()); + slackConnectedComponent = new HashSet<>(lfNetwork.getBuses()); } else { // contingency breaking connectivity + LOGGER.debug("Contingency {} with loss of connectivity", lfContingency.getId()); // we check if factors are still in the main component - Set slackConnectedComponent = lfNetwork.getBuses().stream().filter(Predicate.not(lfContingency.getDisabledBuses()::contains)).collect(Collectors.toSet()); + slackConnectedComponent = new HashSet<>(lfNetwork.getBuses()).stream().filter(Predicate.not(lfContingency.getDisabledBuses()::contains)).collect(Collectors.toSet()); setPredefinedResults(contingencyFactors, slackConnectedComponent); - // 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()) { - List participatingElementsForThisConnectivity = getParticipatingElements( - slackConnectedComponent, lfParameters.getBalanceType(), lfParametersExt); // will also be used to recompute the load flow - postContingencySlackParticipationByBus = participatingElementsForThisConnectivity.stream().collect(Collectors.toMap( - ParticipatingElement::getLfBus, - element -> -element.getFactor(), - Double::sum - )); - } else { - postContingencySlackParticipationByBus = Collections.singletonMap(lfNetwork.getSlackBus(), -1d); - } + // 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(), valueWriter, reporter, hasTransformerBusTargetVoltage); + lfParameters, lfParametersExt, lfContingency.getIndex(), valueWriter, reporter, hasTransformerBusTargetVoltage, hasMultiVariables); networkState.restore(); }); diff --git a/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java index ced9edcb4f..6991ab5a96 100644 --- a/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java @@ -33,7 +33,6 @@ import com.powsybl.openloadflow.network.util.PreviousValueVoltageInitializer; import com.powsybl.openloadflow.network.util.UniformValueVoltageInitializer; import com.powsybl.openloadflow.network.util.VoltageInitializer; -import com.powsybl.openloadflow.util.PerUnit; import com.powsybl.sensitivity.*; import org.apache.commons.lang3.tuple.Pair; @@ -240,11 +239,7 @@ private void createBranchSensitivityValue(LfSensitivityFactor> factorGroups, DenseMatrix factorsState) { @@ -542,88 +537,6 @@ private Map, ConnectivityAnalysisResult> compute return connectivityAnalysisResults; } - private static void addConverterStation(LfNetwork lfNetwork, HvdcConverterStation converterStation, - Set> lccs, Set vscs) { - if (converterStation instanceof VscConverterStation) { - LfVscConverterStationImpl vsc = (LfVscConverterStationImpl) lfNetwork.getGeneratorById(converterStation.getId()); - if (vsc != null) { - vscs.add(vsc); - } - } else { - LfBus bus = lfNetwork.getBusById(converterStation.getTerminal().getBusView().getBus().getId()); - if (bus != null) { - lccs.add(Pair.of(bus, (LccConverterStation) converterStation)); - } - } - } - - private void applyInjectionContingencies(Network network, LfNetwork lfNetwork, PropagatedContingency contingency, - Set participatingGeneratorsToRemove, List busStates, - DcLoadFlowParameters lfParameters) { - // it applies on the network the loss of DC lines contained in the contingency. - // it applies on the network the loss of generators contained in the contingency. - // it applies on the network the loss of loads contained in the contingency. - // Buses state are stored. - - // DC lines. - Set> lccs = new HashSet<>(); - Set vscs = new HashSet<>(); - for (String hvdcId : contingency.getHvdcIdsToOpen()) { - HvdcLine hvdcLine = network.getHvdcLine(hvdcId); - addConverterStation(lfNetwork, hvdcLine.getConverterStation1(), lccs, vscs); - addConverterStation(lfNetwork, hvdcLine.getConverterStation2(), lccs, vscs); - } - - // generators. - Set generators = new HashSet<>(); - for (String generatorId : contingency.getGeneratorIdsToLose()) { - LfGenerator generator = lfNetwork.getGeneratorById(generatorId); - if (generator != null) { // because could not be in main compoment - generators.add((LfGeneratorImpl) generator); - } - } - - for (Map.Entry e : contingency.getLoadIdsToShift().entrySet()) { - LfBus lfBus = lfNetwork.getBusById(e.getKey()); - if (lfBus != null) { // because could not be in main compoment - busStates.add(BusState.save(lfBus)); - } - } - - lccs.stream().map(Pair::getKey).forEach(b -> busStates.add(BusState.save(b))); - vscs.stream().map(LfGenerator::getBus).forEach(b -> busStates.add(BusState.save(b))); - generators.stream().map(LfGenerator::getBus).forEach(b -> busStates.add(BusState.save(b))); - - for (Pair busAndlcc : lccs) { - LfBus bus = busAndlcc.getKey(); - LccConverterStation lcc = busAndlcc.getValue(); - bus.setLoadTargetP(bus.getLoadTargetP() - HvdcConverterStations.getConverterStationTargetP(lcc) / PerUnit.SB); - } - - for (LfVscConverterStationImpl vsc : vscs) { - vsc.setTargetP(0); - } - - boolean distributedSlackOnGenerators = isDistributedSlackOnGenerators(lfParameters); - for (LfGeneratorImpl generator : generators) { - generator.setTargetP(0); - if (distributedSlackOnGenerators && generator.isParticipating()) { - generator.setParticipating(false); - participatingGeneratorsToRemove.add(generator); - } - } - - for (Map.Entry e : contingency.getLoadIdsToShift().entrySet()) { - LfBus lfBus = lfNetwork.getBusById(e.getKey()); - if (lfBus != null) { // because could not be in main compoment - PowerShift shift = e.getValue(); - double p0 = shift.getActive(); - lfBus.setLoadTargetP(lfBus.getLoadTargetP() - LfContingency.getUpdatedLoadP0(lfBus, lfParameters.getBalanceType(), p0, shift.getVariableActive())); - lfBus.getLoads().setAbsVariableLoadTargetP(lfBus.getLoads().getAbsVariableLoadTargetP() - Math.abs(shift.getVariableActive()) * PerUnit.SB); - } - } - } - public DenseMatrix calculateStates(JacobianMatrix j, EquationSystem equationSystem, List> factorGroups, List participatingElements) { @@ -640,67 +553,55 @@ public DenseMatrix calculateStates(JacobianMatrix> factorGroups, boolean hasMultiVariables, DenseMatrix factorStates, DenseMatrix contingenciesStates, - DenseMatrix flowStates, Collection contingencyElements, SensitivityValueWriter valueWriter, Network network, + DenseMatrix flowStates, Collection contingencyElements, SensitivityValueWriter valueWriter, LfNetwork lfNetwork, DcLoadFlowParameters lfParameters, OpenLoadFlowParameters lfParametersExt, JacobianMatrix j, EquationSystem equationSystem, SensitivityFactorHolder factorHolder, List participatingElements, Collection disabledBuses, Collection disabledBranches, Reporter reporter) { List> factors = factorHolder.getFactorsForContingency(contingency.getContingency().getId()); - if (contingency.getHvdcIdsToOpen().isEmpty() && contingency.getGeneratorIdsToLose().isEmpty() && contingency.getLoadIdsToShift().isEmpty()) { + if (contingency.getGeneratorIdsToLose().isEmpty() && contingency.getLoadIdsToShift().isEmpty()) { calculateSensitivityValues(factors, factorStates, contingenciesStates, flowStates, contingencyElements, contingency, valueWriter); } else { - // if we have a contingency including the loss of a DC line or a generator - // if we have a contingency including the loss of a generator // if we have a contingency including the loss of a DC line or a generator or a load - - List busStates = new ArrayList<>(); - Set participatingGeneratorsToRemove = new HashSet<>(); - applyInjectionContingencies(network, lfNetwork, contingency, participatingGeneratorsToRemove, busStates, lfParameters); - - List newParticipatingElements = participatingElements; + // save base state for later restoration after each contingency + NetworkState networkState = NetworkState.save(lfNetwork); + LfContingency lfContingency = contingency.toLfContingency(lfNetwork, true).orElse(null); DenseMatrix newFactorStates = factorStates; - boolean participatingElementsChanged = (isDistributedSlackOnGenerators(lfParameters) && !contingency.getGeneratorIdsToLose().isEmpty()) - || (isDistributedSlackOnLoads(lfParameters) && !contingency.getLoadIdsToShift().isEmpty()); + List newParticipatingElements = participatingElements; + boolean participatingElementsChanged = false; boolean rhsChanged = false; - if (hasMultiVariables) { - Set affectedBuses = new HashSet<>(); - for (var e : contingency.getLoadIdsToShift().entrySet()) { - String busId = e.getKey(); - LfBus bus = lfNetwork.getBusById(busId); - if (bus != null) { - affectedBuses.add(bus); - } + if (lfContingency != null) { + lfContingency.apply(lfParameters.getBalanceType()); + participatingElementsChanged = (isDistributedSlackOnGenerators(lfParameters) && !contingency.getGeneratorIdsToLose().isEmpty()) + || (isDistributedSlackOnLoads(lfParameters) && !contingency.getLoadIdsToShift().isEmpty()); + if (hasMultiVariables) { + Set impactedBuses = lfContingency.getLoadAndGeneratorBuses(); + rhsChanged = rescaleGlsk(factorGroups, impactedBuses); } - for (String id : contingency.getGeneratorIdsToLose()) { - LfBus bus = lfNetwork.getGeneratorById(id).getBus(); - if (bus != null) { - affectedBuses.add(bus); + if (participatingElementsChanged) { + if (isDistributedSlackOnGenerators(lfParameters)) { + // deep copy of participatingElements, removing the participating LfGeneratorImpl whose targetP has been set to 0 + Set participatingGeneratorsToRemove = lfContingency.getLostGenerators(); + newParticipatingElements = participatingElements.stream() + .filter(participatingElement -> !participatingGeneratorsToRemove.contains(participatingElement.getElement())) + .map(participatingElement -> new ParticipatingElement(participatingElement.getElement(), participatingElement.getFactor())) + .collect(Collectors.toList()); + normalizeParticipationFactors(newParticipatingElements, "LfGenerators"); + } else { // slack distribution on loads + newParticipatingElements = getParticipatingElements(lfNetwork.getBuses(), lfParameters.getBalanceType(), lfParametersExt); } } - rhsChanged = rescaleGlsk(factorGroups, affectedBuses); - } - if (participatingElementsChanged) { - if (isDistributedSlackOnGenerators(lfParameters)) { - // deep copy of participatingElements, removing the participating LfGeneratorImpl whose targetP has been set to 0 - newParticipatingElements = participatingElements.stream() - .filter(participatingElement -> !participatingGeneratorsToRemove.contains(participatingElement.getElement())) - .map(participatingElement -> new ParticipatingElement(participatingElement.getElement(), participatingElement.getFactor())) - .collect(Collectors.toList()); - normalizeParticipationFactors(newParticipatingElements, "LfGenerators"); - } else { // slack distribution on loads - newParticipatingElements = getParticipatingElements(lfNetwork.getBuses(), lfParameters.getBalanceType(), lfParametersExt); + if (participatingElementsChanged || rhsChanged) { + newFactorStates = calculateStates(j, equationSystem, factorGroups, newParticipatingElements); } } - if (participatingElementsChanged || rhsChanged) { - newFactorStates = calculateStates(j, equationSystem, factorGroups, newParticipatingElements); - } DenseMatrix newFlowStates = setReferenceActivePowerFlows(lfNetwork, lfParameters, equationSystem, j, factors, - newParticipatingElements, disabledBuses, disabledBranches, reporter); + newParticipatingElements, disabledBuses, disabledBranches, reporter); calculateSensitivityValues(factors, newFactorStates, contingenciesStates, newFlowStates, contingencyElements, contingency, valueWriter); - ElementState.restore(busStates); + networkState.restore(); if (participatingElementsChanged) { setBaseCaseSensitivityValues(factorGroups, factorStates); } @@ -868,7 +769,7 @@ public void analyse(Network network, List contingencies, for (PropagatedContingency contingency : phaseTapChangerContingenciesIndexing.getContingenciesWithoutPhaseTapChangerLoss()) { List contingencyElements = contingency.getBranchIdsToOpen().stream().map(contingencyElementByBranch::get).collect(Collectors.toList()); calculateContingencySensitivityValues(contingency, factorGroups, hasMultiVariables, factorsStates, contingenciesStates, flowStates, contingencyElements, valueWriter, - network, lfNetwork, dcLoadFlowParameters, lfParametersExt, j, equationSystem, validFactorHolder, participatingElements, + lfNetwork, dcLoadFlowParameters, lfParametersExt, j, equationSystem, validFactorHolder, participatingElements, Collections.emptyList(), Collections.emptyList(), reporter); } @@ -884,7 +785,7 @@ public void analyse(Network network, List contingencies, for (PropagatedContingency contingency : propagatedContingencies) { List contingencyElements = contingency.getBranchIdsToOpen().stream().map(contingencyElementByBranch::get).collect(Collectors.toList()); calculateContingencySensitivityValues(contingency, factorGroups, hasMultiVariables, factorsStates, contingenciesStates, flowStates, contingencyElements, valueWriter, - network, lfNetwork, dcLoadFlowParameters, lfParametersExt, j, equationSystem, validFactorHolder, participatingElements, + lfNetwork, dcLoadFlowParameters, lfParametersExt, j, equationSystem, validFactorHolder, participatingElements, Collections.emptyList(), removedPhaseTapChangers, reporter); } } @@ -953,7 +854,7 @@ public void analyse(Network network, List contingencies, for (PropagatedContingency contingency : phaseTapChangerContingenciesIndexing.getContingenciesWithoutPhaseTapChangerLoss()) { Collection contingencyElements = contingency.getBranchIdsToOpen().stream().filter(element -> !elementsToReconnect.contains(element)).map(contingencyElementByBranch::get).collect(Collectors.toList()); calculateContingencySensitivityValues(contingency, factorGroups, hasMultiVariables, factorStateForThisConnectivity, contingenciesStates, flowStates, contingencyElements, valueWriter, - network, lfNetwork, dcLoadFlowParameters, lfParametersExt, j, equationSystem, validFactorHolder, participatingElementsForThisConnectivity, + lfNetwork, dcLoadFlowParameters, lfParametersExt, j, equationSystem, validFactorHolder, participatingElementsForThisConnectivity, disabledBuses, Collections.emptyList(), reporter); } @@ -969,7 +870,7 @@ public void analyse(Network network, List contingencies, for (PropagatedContingency contingency : propagatedContingencies) { Collection contingencyElements = contingency.getBranchIdsToOpen().stream().filter(element -> !elementsToReconnect.contains(element)).map(contingencyElementByBranch::get).collect(Collectors.toList()); calculateContingencySensitivityValues(contingency, factorGroups, hasMultiVariables, factorStateForThisConnectivity, contingenciesStates, flowStates, contingencyElements, valueWriter, - network, lfNetwork, dcLoadFlowParameters, lfParametersExt, j, equationSystem, validFactorHolder, participatingElementsForThisConnectivity, + lfNetwork, dcLoadFlowParameters, lfParametersExt, j, equationSystem, validFactorHolder, participatingElementsForThisConnectivity, disabledBuses, disabledPhaseTapChangers, reporter); } } diff --git a/src/main/java/com/powsybl/openloadflow/sensi/OpenSensitivityAnalysisProvider.java b/src/main/java/com/powsybl/openloadflow/sensi/OpenSensitivityAnalysisProvider.java index 6f7b66e9c7..ccf6589436 100644 --- a/src/main/java/com/powsybl/openloadflow/sensi/OpenSensitivityAnalysisProvider.java +++ b/src/main/java/com/powsybl/openloadflow/sensi/OpenSensitivityAnalysisProvider.java @@ -125,7 +125,8 @@ public CompletableFuture run(Network network, "Sensitivity analysis on network ${networkId}", "networkId", network.getId()); List propagatedContingencies = PropagatedContingency.createListForSensitivityAnalysis(network, contingencies, - sensitivityAnalysisParameters.getLoadFlowParameters().getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD); + sensitivityAnalysisParameters.getLoadFlowParameters().getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD, + sensitivityAnalysisParameters.getLoadFlowParameters().isHvdcAcEmulation() && !sensitivityAnalysisParameters.getLoadFlowParameters().isDc()); LoadFlowParameters lfParameters = sensitivityAnalysisParameters.getLoadFlowParameters(); OpenLoadFlowParameters lfParametersExt = OpenLoadFlowParameters.get(lfParameters); diff --git a/src/test/java/com/powsybl/openloadflow/sa/LfContingencyTest.java b/src/test/java/com/powsybl/openloadflow/sa/LfContingencyTest.java index f7589a7f35..7e091c8946 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/LfContingencyTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/LfContingencyTest.java @@ -73,7 +73,7 @@ void test() throws IOException { String branchId = "LINE_S3S4"; Contingency contingency = new Contingency(branchId, new BranchContingency(branchId)); List propagatedContingencies = - PropagatedContingency.createListForSecurityAnalysis(network, Collections.singletonList(contingency), new HashSet<>(), false, false); + PropagatedContingency.createListForSecurityAnalysis(network, Collections.singletonList(contingency), new HashSet<>(), false, false, false); List lfContingencies = propagatedContingencies.stream() .flatMap(propagatedContingency -> propagatedContingency.toLfContingency(mainNetwork, true).stream()) @@ -104,7 +104,7 @@ void testGeneratorNotFound() { String generatorId = "GEN"; Contingency contingency = new Contingency(generatorId, new GeneratorContingency(generatorId)); assertThrows(PowsyblException.class, () -> - PropagatedContingency.createListForSecurityAnalysis(network, Collections.singletonList(contingency), new HashSet<>(), false, false), + PropagatedContingency.createListForSecurityAnalysis(network, Collections.singletonList(contingency), new HashSet<>(), false, false, false), "Generator 'GEN' not found in the network"); } @@ -122,7 +122,7 @@ void testLoadNotFound() { String loadId = "LOAD"; Contingency contingency = new Contingency(loadId, new LoadContingency(loadId)); assertThrows(PowsyblException.class, () -> - PropagatedContingency.createListForSecurityAnalysis(network, Collections.singletonList(contingency), new HashSet<>(), false, false), + PropagatedContingency.createListForSecurityAnalysis(network, Collections.singletonList(contingency), new HashSet<>(), false, false, false), "Load 'LOAD' not found in the network"); } } diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisGraphTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisGraphTest.java index fca5d6a2d7..9bfca78443 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisGraphTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisGraphTest.java @@ -152,7 +152,8 @@ List> getLoadFlowContingencies(GraphDecrementalConnectivityF long start = System.currentTimeMillis(); Set allSwitchesToOpen = new HashSet<>(); List propagatedContingencies = PropagatedContingency.createListForSecurityAnalysis(network, contingencies, allSwitchesToOpen, - lfParameters.isShuntCompensatorVoltageControlOn(), lfParameters.getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD); + lfParameters.isShuntCompensatorVoltageControlOn(), lfParameters.getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD, + lfParameters.isHvdcAcEmulation() && !lfParameters.isDc()); LOGGER.info("Contingencies contexts calculated from contingencies in {} ms", System.currentTimeMillis() - start); AcLoadFlowParameters acParameters = OpenLoadFlowParameters.createAcParameters(network, diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index ef419d8c7e..4d511377ef 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -16,15 +16,20 @@ import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.iidm.network.test.FourSubstationsNodeBreakerFactory; import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.loadflow.LoadFlowResult; import com.powsybl.math.matrix.DenseMatrixFactory; +import com.powsybl.math.matrix.MatrixFactory; import com.powsybl.openloadflow.OpenLoadFlowParameters; +import com.powsybl.openloadflow.OpenLoadFlowProvider; import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory; +import com.powsybl.openloadflow.graph.GraphDecrementalConnectivityFactory; import com.powsybl.openloadflow.network.*; import com.powsybl.openloadflow.util.LoadFlowAssert; import com.powsybl.security.*; import com.powsybl.security.detectors.DefaultLimitViolationDetector; import com.powsybl.security.monitor.StateMonitor; import com.powsybl.security.results.*; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -43,6 +48,22 @@ */ class OpenSecurityAnalysisTest { + private ComputationManager computationManager; + + private OpenSecurityAnalysisProvider securityAnalysisProvider; + + private OpenLoadFlowProvider loadFlowProvider; + + @BeforeEach + void setUp() { + computationManager = Mockito.mock(ComputationManager.class); + Mockito.when(computationManager.getExecutor()).thenReturn(ForkJoinPool.commonPool()); + MatrixFactory matrixFactory = new DenseMatrixFactory(); + GraphDecrementalConnectivityFactory connectivityFactory = new EvenShiloachGraphDecrementalConnectivityFactory<>(); + securityAnalysisProvider = new OpenSecurityAnalysisProvider(matrixFactory, connectivityFactory); + loadFlowProvider = new OpenLoadFlowProvider(matrixFactory, connectivityFactory); + } + private static Network createNodeBreakerNetwork() { Network network = NodeBreakerNetworkFactory.create(); @@ -82,23 +103,25 @@ private static Network createNodeBreakerNetwork() { return network; } + private LoadFlowResult runLoadFlow(Network network, LoadFlowParameters parameters) { + return loadFlowProvider.run(network, computationManager, network.getVariantManager().getWorkingVariantId(), parameters) + .join(); + } + /** * Runs a security analysis with default parameters + most meshed slack bus selection */ - private static SecurityAnalysisResult runSecurityAnalysis(Network network, List contingencies, List monitors, - LoadFlowParameters lfParameters) { + private SecurityAnalysisResult runSecurityAnalysis(Network network, List contingencies, List monitors, + LoadFlowParameters lfParameters) { SecurityAnalysisParameters securityAnalysisParameters = new SecurityAnalysisParameters(); securityAnalysisParameters.setLoadFlowParameters(lfParameters); return runSecurityAnalysis(network, contingencies, monitors, securityAnalysisParameters); } - private static SecurityAnalysisResult runSecurityAnalysis(Network network, List contingencies, List monitors, - SecurityAnalysisParameters saParameters) { + private SecurityAnalysisResult runSecurityAnalysis(Network network, List contingencies, List monitors, + SecurityAnalysisParameters saParameters) { ContingenciesProvider provider = n -> contingencies; - var saProvider = new OpenSecurityAnalysisProvider(new DenseMatrixFactory(), new EvenShiloachGraphDecrementalConnectivityFactory<>()); - var computationManager = Mockito.mock(ComputationManager.class); - Mockito.when(computationManager.getExecutor()).thenReturn(ForkJoinPool.commonPool()); - SecurityAnalysisReport report = saProvider.run(network, + SecurityAnalysisReport report = securityAnalysisProvider.run(network, network.getVariantManager().getWorkingVariantId(), new DefaultLimitViolationDetector(), new LimitViolationFilter(), @@ -111,19 +134,19 @@ private static SecurityAnalysisResult runSecurityAnalysis(Network network, List< return report.getResult(); } - private static SecurityAnalysisResult runSecurityAnalysis(Network network, List contingencies, List monitors) { + private SecurityAnalysisResult runSecurityAnalysis(Network network, List contingencies, List monitors) { return runSecurityAnalysis(network, contingencies, monitors, new LoadFlowParameters()); } - private static SecurityAnalysisResult runSecurityAnalysis(Network network, List contingencies, LoadFlowParameters loadFlowParameters) { + private SecurityAnalysisResult runSecurityAnalysis(Network network, List contingencies, LoadFlowParameters loadFlowParameters) { return runSecurityAnalysis(network, contingencies, Collections.emptyList(), loadFlowParameters); } - private static SecurityAnalysisResult runSecurityAnalysis(Network network, List contingencies) { + private SecurityAnalysisResult runSecurityAnalysis(Network network, List contingencies) { return runSecurityAnalysis(network, contingencies, Collections.emptyList()); } - private static SecurityAnalysisResult runSecurityAnalysis(Network network) { + private SecurityAnalysisResult runSecurityAnalysis(Network network) { return runSecurityAnalysis(network, Collections.emptyList(), Collections.emptyList()); } @@ -959,6 +982,25 @@ void testSaWithShuntContingency3() { assertEquals(42.792, contingencyResult2.getBranchResult("tr3").getQ2(), LoadFlowAssert.DELTA_POWER); } + @Test + void testSaWithShuntContingency4() { + Network network = VoltageControlNetworkFactory.createWithShuntSharedRemoteControl(); + network.getShuntCompensatorStream().forEach(shuntCompensator -> { + shuntCompensator.setSectionCount(10); + }); + + LoadFlowParameters lfParameters = new LoadFlowParameters() + .setShuntCompensatorVoltageControlOn(true); + + List contingencies = List.of(new Contingency("SHUNT4", new ShuntCompensatorContingency("SHUNT4")), + new Contingency("tr3", new BranchContingency("tr3"))); + + List monitors = createAllBranchesMonitors(network); + + CompletionException exception = assertThrows(CompletionException.class, () -> runSecurityAnalysis(network, contingencies, monitors, lfParameters)); + assertEquals("Shunt compensator 'SHUNT4' not found in the network", exception.getCause().getMessage()); + } + @Test void testDcSaWithLoadContingency() { Network network = DistributedSlackNetworkFactory.createNetworkWithLoads(); @@ -1308,7 +1350,6 @@ void testHvdcAcEmulation() { contingencies.add(Contingency.line("l12")); contingencies.add(Contingency.line("l46")); contingencies.add(Contingency.generator("g1")); - // FIXME: note that the HVDC line contingency is not supported yet. List monitors = createAllBranchesMonitors(network); @@ -1320,6 +1361,87 @@ void testHvdcAcEmulation() { // post-contingency tests PostContingencyResult g1ContingencyResult = getPostContingencyResult(result, "g1"); assertEquals(-0.696, g1ContingencyResult.getBranchResult("l25").getP1(), LoadFlowAssert.DELTA_POWER); + + List contingencies2 = new ArrayList<>(); + contingencies2.add(Contingency.hvdcLine("hvdc34")); + contingencies2.add(Contingency.generator("g1")); + SecurityAnalysisResult result2 = runSecurityAnalysis(network, contingencies2, monitors, parameters); + + // post-contingency tests + PostContingencyResult hvdcContingencyResult = getPostContingencyResult(result2, "hvdc34"); + assertEquals(-0.99999, hvdcContingencyResult.getBranchResult("l25").getP1(), LoadFlowAssert.DELTA_POWER); + + PostContingencyResult g1ContingencyResult2 = getPostContingencyResult(result, "g1"); + assertEquals(-0.696, g1ContingencyResult2.getBranchResult("l25").getP1(), LoadFlowAssert.DELTA_POWER); + } + + @Test + void testContingencyOnHvdcLcc() { + Network network = HvdcNetworkFactory.createTwoCcLinkedByAHvdcWithGenerators(); + + LoadFlowParameters parameters = new LoadFlowParameters(); + parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD); + OpenLoadFlowParameters openLoadFlowParameters = new OpenLoadFlowParameters(); + openLoadFlowParameters.setSlackBusPMaxMismatch(0.0001); + parameters.addExtension(OpenLoadFlowParameters.class, openLoadFlowParameters); + + List contingencies = List.of(new Contingency("hvdc34", new HvdcLineContingency("hvdc34"))); + + List monitors = createAllBranchesMonitors(network); + + SecurityAnalysisResult result = runSecurityAnalysis(network, contingencies, monitors, parameters); + + network.getHvdcLine("hvdc34").getConverterStation1().getTerminal().disconnect(); + network.getHvdcLine("hvdc34").getConverterStation2().getTerminal().disconnect(); + runLoadFlow(network, parameters); + + PreContingencyResult preContingencyResult = result.getPreContingencyResult(); + assertEquals(1.360, preContingencyResult.getPreContingencyBranchResult("l12").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(-0.360, preContingencyResult.getPreContingencyBranchResult("l13").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(-1.596, preContingencyResult.getPreContingencyBranchResult("l23").getP1(), LoadFlowAssert.DELTA_POWER); + + PostContingencyResult postContingencyResult = getPostContingencyResult(result, "hvdc34"); + assertEquals(network.getLine("l12").getTerminal1().getP(), postContingencyResult.getBranchResult("l12").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(network.getLine("l12").getTerminal1().getQ(), postContingencyResult.getBranchResult("l12").getQ1(), LoadFlowAssert.DELTA_POWER); + assertEquals(network.getLine("l13").getTerminal1().getP(), postContingencyResult.getBranchResult("l13").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(network.getLine("l13").getTerminal1().getQ(), postContingencyResult.getBranchResult("l13").getQ1(), LoadFlowAssert.DELTA_POWER); + assertEquals(network.getLine("l23").getTerminal1().getP(), postContingencyResult.getBranchResult("l23").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(network.getLine("l23").getTerminal1().getQ(), postContingencyResult.getBranchResult("l23").getQ1(), LoadFlowAssert.DELTA_POWER); + } + + @Test + void testContingencyOnHvdcVsc() { + Network network = HvdcNetworkFactory.createTwoCcLinkedByAHvdcVscWithGenerators(); + network.getGeneratorStream().forEach(gen -> gen.setMaxP(2 * gen.getMaxP())); + + LoadFlowParameters parameters = new LoadFlowParameters(); + parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_GENERATION_P_MAX); + OpenLoadFlowParameters openLoadFlowParameters = new OpenLoadFlowParameters(); + openLoadFlowParameters.setSlackBusPMaxMismatch(0.0001); + parameters.addExtension(OpenLoadFlowParameters.class, openLoadFlowParameters); + + List contingencies = List.of(new Contingency("hvdc34", new HvdcLineContingency("hvdc34"))); + + List monitors = createAllBranchesMonitors(network); + + SecurityAnalysisResult result = runSecurityAnalysis(network, contingencies, monitors, parameters); + + network.getHvdcLine("hvdc34").getConverterStation1().getTerminal().disconnect(); + network.getHvdcLine("hvdc34").getConverterStation2().getTerminal().disconnect(); + runLoadFlow(network, parameters); + + PreContingencyResult preContingencyResult = result.getPreContingencyResult(); + assertEquals(1.25, preContingencyResult.getPreContingencyBranchResult("l12").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(-0.228, preContingencyResult.getPreContingencyBranchResult("l13").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(-1.727, preContingencyResult.getPreContingencyBranchResult("l23").getP1(), LoadFlowAssert.DELTA_POWER); + + PostContingencyResult postContingencyResult = getPostContingencyResult(result, "hvdc34"); + assertEquals(network.getLine("l12").getTerminal1().getP(), postContingencyResult.getBranchResult("l12").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(network.getLine("l12").getTerminal1().getQ(), postContingencyResult.getBranchResult("l12").getQ1(), LoadFlowAssert.DELTA_POWER); + assertEquals(network.getLine("l13").getTerminal1().getP(), postContingencyResult.getBranchResult("l13").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(network.getLine("l13").getTerminal1().getQ(), postContingencyResult.getBranchResult("l13").getQ1(), LoadFlowAssert.DELTA_POWER); + assertEquals(network.getLine("l23").getTerminal1().getP(), postContingencyResult.getBranchResult("l23").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(network.getLine("l23").getTerminal1().getQ(), postContingencyResult.getBranchResult("l23").getQ1(), LoadFlowAssert.DELTA_POWER); } @Test diff --git a/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisContingenciesTest.java b/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisContingenciesTest.java index 4f1c9ab458..02e9ee2ed9 100644 --- a/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisContingenciesTest.java +++ b/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisContingenciesTest.java @@ -11,6 +11,7 @@ import com.powsybl.iidm.network.Branch; import com.powsybl.iidm.network.Line; import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControlAdder; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.loadflow.LoadFlowParameters; import com.powsybl.openloadflow.OpenLoadFlowParameters; @@ -18,14 +19,12 @@ import com.powsybl.openloadflow.sensi.AbstractSensitivityAnalysisTest; import com.powsybl.openloadflow.util.LoadFlowAssert; import com.powsybl.sensitivity.*; -import org.apache.commons.lang3.NotImplementedException; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletionException; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -590,24 +589,6 @@ void testHvdcSensiRescale() { // assertEquals(loadFlowDiff.get("l56"), hvdcWriter.getBranchFlow1SensitivityValue(Pair.of("hvdc34", "l56"), "l25"), LoadFlowAssert.DELTA_POWER); } - @Test - void testContingencyOnHvdc() { - Network network = HvdcNetworkFactory.createTwoCcLinkedByAHvdcVscWithGenerators(); - - SensitivityAnalysisParameters sensiParameters = createParameters(false, "b1_vl_0", true); - - List factors = createFactorMatrix(Stream.of("g1", "g2").map(network::getGenerator).collect(Collectors.toList()), - Stream.of("l12", "l13", "l23").map(network::getBranch).collect(Collectors.toList())); - - List contingencies = List.of(new Contingency("hvdc34", new HvdcLineContingency("hvdc34"))); - - List variableSets = Collections.emptyList(); - - CompletionException e = assertThrows(CompletionException.class, () -> sensiRunner.run(network, factors, contingencies, variableSets, sensiParameters)); - assertTrue(e.getCause() instanceof NotImplementedException); - assertEquals("Contingencies on a DC line are not yet supported in AC mode.", e.getCause().getMessage()); - } - @Test void testContingencyPropagationLfSwitch() { SensitivityAnalysisParameters sensiParameters = createParameters(false); @@ -791,4 +772,226 @@ void testLosingALineButBothEndsInMainComponent() { assertEquals(0.0, result.getBranchFlow1SensitivityValue("l34+l12", "g3", "l12"), LoadFlowAssert.DELTA_POWER); assertEquals(Double.NaN, result.getBranchFlow1FunctionReferenceValue("l34+l12", "l12"), LoadFlowAssert.DELTA_POWER); } + + @Test + void testTrivialContingencyOnGenerator() { + Network network = ConnectedComponentNetworkFactory.createTwoCcLinkedByTwoLines(); + + SensitivityAnalysisParameters sensiParameters = createParameters(false, "b1", true); + sensiParameters.setLoadFlowParameters(new LoadFlowParameters() + .setDc(false) + .setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_GENERATION_P_MAX)); + + List factors = createFactorMatrix(Stream.of("g2").map(network::getGenerator).collect(Collectors.toList()), + Stream.of("l12", "l13", "l23").map(network::getBranch).collect(Collectors.toList())); + + List contingencies = List.of(new Contingency("g6", new GeneratorContingency("g6"))); + + SensitivityAnalysisResult result = sensiRunner.run(network, factors, contingencies, Collections.emptyList(), sensiParameters); + + assertEquals(3, result.getValues("g6").size()); + assertEquals(0, result.getBranchFlow1SensitivityValue("g6", "g2", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(0, result.getBranchFlow1SensitivityValue("g6", "g2", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(0, result.getBranchFlow1SensitivityValue("g6", "g2", "l23"), LoadFlowAssert.DELTA_POWER); + + assertEquals(-1.3365d, result.getBranchFlow1FunctionReferenceValue("g6", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(0.3364d, result.getBranchFlow1FunctionReferenceValue("g6", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(1.6664, result.getBranchFlow1FunctionReferenceValue("g6", "l23"), LoadFlowAssert.DELTA_POWER); + } + + @Test + void testContingencyOnLoad() { + Network network = ConnectedComponentNetworkFactory.createTwoCcLinkedByTwoLines(); + + SensitivityAnalysisParameters sensiParameters = createParameters(false, "b1", false); + sensiParameters.setLoadFlowParameters(new LoadFlowParameters() + .setDc(false) + .setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD)); + + List factors = createFactorMatrix(Stream.of("g2").map(network::getGenerator).collect(Collectors.toList()), + Stream.of("l12", "l13", "l23").map(network::getBranch).collect(Collectors.toList())); + + List contingencies = List.of(new Contingency("d5", new LoadContingency("d5"))); + SensitivityAnalysisResult result = sensiRunner.run(network, factors, contingencies, Collections.emptyList(), sensiParameters); + + network.getLoad("d5").getTerminal().disconnect(); + SensitivityAnalysisResult result2 = sensiRunner.run(network, factors, Collections.emptyList(), Collections.emptyList(), sensiParameters); + + assertEquals(3, result.getValues("d5").size()); + assertEquals(result2.getBranchFlow1SensitivityValue("g2", "l12"), result.getBranchFlow1SensitivityValue("d5", "g2", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g2", "l13"), result.getBranchFlow1SensitivityValue("d5", "g2", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g2", "l23"), result.getBranchFlow1SensitivityValue("d5", "g2", "l23"), LoadFlowAssert.DELTA_POWER); + + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l12"), result.getBranchFlow1FunctionReferenceValue("d5", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l13"), result.getBranchFlow1FunctionReferenceValue("d5", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l23"), result.getBranchFlow1FunctionReferenceValue("d5", "l23"), LoadFlowAssert.DELTA_POWER); + } + + @Test + void testGLSK() { + Network network = FourBusNetworkFactory.create(); + network.getGeneratorStream().forEach(gen -> gen.setMaxP(2 * gen.getMaxP())); + + SensitivityAnalysisParameters sensiParameters = createParameters(false, "b1_vl_0", true); + + List variables = List.of(new WeightedSensitivityVariable("d2", 30f), + new WeightedSensitivityVariable("g2", 10f), + new WeightedSensitivityVariable("d3", 50f), + new WeightedSensitivityVariable("g1", 10f)); + List variableSets = Collections.singletonList(new SensitivityVariableSet("glsk", variables)); + + List factors = network.getBranchStream().map(branch -> createBranchFlowPerLinearGlsk(branch.getId(), "glsk")).collect(Collectors.toList()); + + List contingencies = List.of(new Contingency("g1", new GeneratorContingency("g1"))); + + SensitivityAnalysisResult result = sensiRunner.run(network, factors, contingencies, variableSets, sensiParameters); + + network.getGenerator("g1").getTerminal().disconnect(); + SensitivityAnalysisResult result2 = sensiRunner.run(network, factors, Collections.emptyList(), variableSets, sensiParameters); + + assertEquals(result2.getBranchFlow1SensitivityValue("glsk", "l14"), result.getBranchFlow1SensitivityValue("g1", "glsk", "l14"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("glsk", "l12"), result.getBranchFlow1SensitivityValue("g1", "glsk", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("glsk", "l23"), result.getBranchFlow1SensitivityValue("g1", "glsk", "l23"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("glsk", "l34"), result.getBranchFlow1SensitivityValue("g1", "glsk", "l34"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("glsk", "l34"), result.getBranchFlow1SensitivityValue("g1", "glsk", "l34"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("glsk", "l13"), result.getBranchFlow1SensitivityValue("g1", "glsk", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l14"), result.getBranchFlow1FunctionReferenceValue("g1", "l14"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l12"), result.getBranchFlow1FunctionReferenceValue("g1", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l23"), result.getBranchFlow1FunctionReferenceValue("g1", "l23"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l34"), result.getBranchFlow1FunctionReferenceValue("g1", "l34"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l34"), result.getBranchFlow1FunctionReferenceValue("g1", "l34"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l13"), result.getBranchFlow1FunctionReferenceValue("g1", "l13"), LoadFlowAssert.DELTA_POWER); + } + + @Test + void testContingencyOnBranch() { + Network network = ConnectedComponentNetworkFactory.createTwoCcLinkedByTwoLines(); + + SensitivityAnalysisParameters sensiParameters = createParameters(false, "b1", true); + sensiParameters.setLoadFlowParameters(new LoadFlowParameters() + .setDc(false) + .setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD)); + + List factors = createFactorMatrix(Stream.of("g2").map(network::getGenerator).collect(Collectors.toList()), + Stream.of("l12", "l13").map(network::getBranch).collect(Collectors.toList())); + + List contingencies = List.of(new Contingency("l23", new BranchContingency("l23"))); + + SensitivityAnalysisResult result = sensiRunner.run(network, factors, contingencies, Collections.emptyList(), sensiParameters); + + network.getLine("l23").getTerminal1().disconnect(); + network.getLine("l23").getTerminal2().disconnect(); + SensitivityAnalysisResult result2 = sensiRunner.run(network, factors, Collections.emptyList(), Collections.emptyList(), sensiParameters); + + assertEquals(result2.getBranchFlow1SensitivityValue("g2", "l12"), result.getBranchFlow1SensitivityValue("l23", "g2", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g2", "l13"), result.getBranchFlow1SensitivityValue("l23", "g2", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l12"), result.getBranchFlow1FunctionReferenceValue("l23", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l13"), result.getBranchFlow1FunctionReferenceValue("l23", "l13"), LoadFlowAssert.DELTA_POWER); + } + + @Test + void testContingencyOnHvdcLcc() { + Network network = HvdcNetworkFactory.createTwoCcLinkedByAHvdcWithGenerators(); + network.getGeneratorStream().forEach(gen -> gen.setMaxP(3 * gen.getMaxP())); + + SensitivityAnalysisParameters sensiParameters = createParameters(false, "b1_vl_0", true); + sensiParameters.getLoadFlowParameters().getExtension(OpenLoadFlowParameters.class).setSlackBusPMaxMismatch(0.001); + + List factors = createFactorMatrix(Stream.of("g1", "g2").map(network::getGenerator).collect(Collectors.toList()), + Stream.of("l12", "l13", "l23").map(network::getBranch).collect(Collectors.toList())); + + List contingencies = List.of(new Contingency("hvdc34", new HvdcLineContingency("hvdc34"))); + + SensitivityAnalysisResult result = sensiRunner.run(network, factors, contingencies, Collections.emptyList(), sensiParameters); + + network.getHvdcLine("hvdc34").getConverterStation1().getTerminal().disconnect(); + network.getHvdcLine("hvdc34").getConverterStation2().getTerminal().disconnect(); + SensitivityAnalysisResult result2 = sensiRunner.run(network, factors, Collections.emptyList(), Collections.emptyList(), sensiParameters); + + List contingencyResult = result.getValues("hvdc34"); + assertEquals(6, contingencyResult.size()); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l12"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l13"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l23"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l23"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g2", "l12"), result.getBranchFlow1SensitivityValue("hvdc34", "g2", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g2", "l13"), result.getBranchFlow1SensitivityValue("hvdc34", "g2", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g2", "l23"), result.getBranchFlow1SensitivityValue("hvdc34", "g2", "l23"), LoadFlowAssert.DELTA_POWER); + + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l12"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l13"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l23"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l23"), LoadFlowAssert.DELTA_POWER); + } + + @Test + void testContingencyOnHvdcVsc() { + Network network = HvdcNetworkFactory.createTwoCcLinkedByAHvdcVscWithGenerators(); + network.getGeneratorStream().forEach(gen -> gen.setMaxP(2 * gen.getMaxP())); + + SensitivityAnalysisParameters sensiParameters = createParameters(false, "b1_vl_0", true); + sensiParameters.getLoadFlowParameters().getExtension(OpenLoadFlowParameters.class).setSlackBusPMaxMismatch(0.001); + + List factors = createFactorMatrix(Stream.of("g1", "g2").map(network::getGenerator).collect(Collectors.toList()), + Stream.of("l12", "l13", "l23").map(network::getBranch).collect(Collectors.toList())); + + List contingencies = List.of(new Contingency("hvdc34", new HvdcLineContingency("hvdc34"))); + + SensitivityAnalysisResult result = sensiRunner.run(network, factors, contingencies, Collections.emptyList(), sensiParameters); + + network.getHvdcLine("hvdc34").getConverterStation1().getTerminal().disconnect(); + network.getHvdcLine("hvdc34").getConverterStation2().getTerminal().disconnect(); + SensitivityAnalysisResult result2 = sensiRunner.run(network, factors, Collections.emptyList(), Collections.emptyList(), sensiParameters); + + assertEquals(6, result.getValues("hvdc34").size()); + List contingencyResult = result.getValues("hvdc34"); + assertEquals(6, contingencyResult.size()); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l12"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l13"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l23"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l23"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g2", "l12"), result.getBranchFlow1SensitivityValue("hvdc34", "g2", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g2", "l13"), result.getBranchFlow1SensitivityValue("hvdc34", "g2", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g2", "l23"), result.getBranchFlow1SensitivityValue("hvdc34", "g2", "l23"), LoadFlowAssert.DELTA_POWER); + + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l12"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l13"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l23"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l23"), LoadFlowAssert.DELTA_POWER); + } + + @Test + void testContingencyOnHvdcVsc2() { + Network network = HvdcNetworkFactory.createWithHvdcInAcEmulation(); + network.getGeneratorStream().forEach(gen -> gen.setMaxP(2 * gen.getMaxP())); + network.getHvdcLine("hvdc34").newExtension(HvdcAngleDroopActivePowerControlAdder.class) + .withDroop(180) + .withP0(0.f) + .withEnabled(true) + .add(); + + SensitivityAnalysisParameters sensiParameters = createParameters(false, "b1_vl_0", true); + sensiParameters.getLoadFlowParameters().getExtension(OpenLoadFlowParameters.class).setSlackBusPMaxMismatch(0.001); + + List factors = createFactorMatrix(Stream.of("g1", "g5").map(network::getGenerator).collect(Collectors.toList()), + Stream.of("l12", "l25", "l56").map(network::getBranch).collect(Collectors.toList())); + + List contingencies = List.of(new Contingency("hvdc34", new HvdcLineContingency("hvdc34"))); + + SensitivityAnalysisResult result = sensiRunner.run(network, factors, contingencies, Collections.emptyList(), sensiParameters); + + network.getHvdcLine("hvdc34").getConverterStation1().getTerminal().disconnect(); + network.getHvdcLine("hvdc34").getConverterStation2().getTerminal().disconnect(); + SensitivityAnalysisResult result2 = sensiRunner.run(network, factors, Collections.emptyList(), Collections.emptyList(), sensiParameters); + + assertEquals(6, result.getValues("hvdc34").size()); + List contingencyResult = result.getValues("hvdc34"); + assertEquals(6, contingencyResult.size()); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l12"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l25"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l25"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l56"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l56"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g5", "l12"), result.getBranchFlow1SensitivityValue("hvdc34", "g5", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g5", "l25"), result.getBranchFlow1SensitivityValue("hvdc34", "g5", "l25"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g5", "l56"), result.getBranchFlow1SensitivityValue("hvdc34", "g5", "l56"), LoadFlowAssert.DELTA_POWER); + + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l12"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l25"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l25"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l56"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l56"), LoadFlowAssert.DELTA_POWER); + } } diff --git a/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisContingenciesTest.java b/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisContingenciesTest.java index b137764b2e..24dac6dd83 100644 --- a/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisContingenciesTest.java +++ b/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisContingenciesTest.java @@ -11,8 +11,10 @@ import com.powsybl.commons.reporter.Reporter; import com.powsybl.contingency.*; import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControlAdder; import com.powsybl.iidm.network.test.PhaseShifterTestCaseFactory; import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.openloadflow.OpenLoadFlowParameters; import com.powsybl.openloadflow.network.*; import com.powsybl.openloadflow.sensi.AbstractSensitivityAnalysisTest; import com.powsybl.openloadflow.sensi.OpenSensitivityAnalysisParameters; @@ -2037,4 +2039,43 @@ void testLosingALineButBothEndsInMainComponent() { assertEquals(0.0, result.getBranchFlow1SensitivityValue("l34+l12", "g3", "l12"), LoadFlowAssert.DELTA_POWER); assertEquals(Double.NaN, result.getBranchFlow1FunctionReferenceValue("l34+l12", "l12"), LoadFlowAssert.DELTA_POWER); } + + @Test + void testContingencyOnHvdcInAcEmulation() { + Network network = HvdcNetworkFactory.createWithHvdcInAcEmulation(); + network.getGeneratorStream().forEach(gen -> gen.setMaxP(2 * gen.getMaxP())); + network.getHvdcLine("hvdc34").newExtension(HvdcAngleDroopActivePowerControlAdder.class) + .withDroop(180) + .withP0(0.f) + .withEnabled(true) + .add(); + + SensitivityAnalysisParameters sensiParameters = createParameters(true, "b1_vl_0", true); + sensiParameters.getLoadFlowParameters().getExtension(OpenLoadFlowParameters.class).setSlackBusPMaxMismatch(0.001); + + List factors = createFactorMatrix(Stream.of("g1", "g5").map(network::getGenerator).collect(Collectors.toList()), + Stream.of("l12", "l25", "l56").map(network::getBranch).collect(Collectors.toList())); + + List contingencies = List.of(new Contingency("hvdc34", new HvdcLineContingency("hvdc34"))); + + SensitivityAnalysisResult result = sensiRunner.run(network, factors, contingencies, Collections.emptyList(), sensiParameters); + + network.getHvdcLine("hvdc34").getConverterStation1().getTerminal().disconnect(); + network.getHvdcLine("hvdc34").getConverterStation2().getTerminal().disconnect(); + SensitivityAnalysisResult result2 = sensiRunner.run(network, factors, Collections.emptyList(), Collections.emptyList(), sensiParameters); + + assertEquals(6, result.getValues("hvdc34").size()); + List contingencyResult = result.getValues("hvdc34"); + assertEquals(6, contingencyResult.size()); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l12"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l25"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l25"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g1", "l56"), result.getBranchFlow1SensitivityValue("hvdc34", "g1", "l56"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g5", "l12"), result.getBranchFlow1SensitivityValue("hvdc34", "g5", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g5", "l25"), result.getBranchFlow1SensitivityValue("hvdc34", "g5", "l25"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1SensitivityValue("g5", "l56"), result.getBranchFlow1SensitivityValue("hvdc34", "g5", "l56"), LoadFlowAssert.DELTA_POWER); + + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l12"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l25"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l25"), LoadFlowAssert.DELTA_POWER); + assertEquals(result2.getBranchFlow1FunctionReferenceValue(null, "l56"), result.getBranchFlow1FunctionReferenceValue("hvdc34", "l56"), LoadFlowAssert.DELTA_POWER); + } } diff --git a/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisTest.java index 7d92ebf7d8..2bebd26cb4 100644 --- a/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisTest.java @@ -829,7 +829,7 @@ void testContingencyOnOpenLine() { Network network = NodeBreakerNetworkFactory.create(); List contingencies = List.of(new Contingency("c1", new BranchContingency("L1"))); - List propagatedContingencies = PropagatedContingency.createListForSensitivityAnalysis(network, contingencies, false); + List propagatedContingencies = PropagatedContingency.createListForSensitivityAnalysis(network, contingencies, false, false); assertEquals(1, propagatedContingencies.size()); }