diff --git a/src/main/java/com/powsybl/openloadflow/AcLoadFlowFromCache.java b/src/main/java/com/powsybl/openloadflow/AcLoadFlowFromCache.java new file mode 100644 index 0000000000..569884c72a --- /dev/null +++ b/src/main/java/com/powsybl/openloadflow/AcLoadFlowFromCache.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.openloadflow; + +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.iidm.network.Network; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowContext; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowResult; +import com.powsybl.openloadflow.ac.outerloop.AcloadFlowEngine; +import com.powsybl.openloadflow.network.impl.LfNetworkList; +import com.powsybl.openloadflow.network.impl.Networks; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author Geoffroy Jamgotchian + */ +public class AcLoadFlowFromCache { + + private final Network network; + + private final LoadFlowParameters parameters; + + private final AcLoadFlowParameters acParameters; + + private final Reporter reporter; + + public AcLoadFlowFromCache(Network network, LoadFlowParameters parameters, AcLoadFlowParameters acParameters, Reporter reporter) { + this.network = Objects.requireNonNull(network); + this.parameters = Objects.requireNonNull(parameters); + this.acParameters = Objects.requireNonNull(acParameters); + this.reporter = Objects.requireNonNull(reporter); + } + + private List initContexts(NetworkCache.Entry entry) { + List contexts; + try (LfNetworkList lfNetworkList = Networks.load(network, acParameters.getNetworkParameters(), Collections.emptySet(), Collections.emptySet(), reporter)) { + contexts = lfNetworkList.getList() + .stream() + .map(n -> new AcLoadFlowContext(n, acParameters)) + .collect(Collectors.toList()); + entry.setContexts(contexts); + entry.setVariantCleaner(lfNetworkList.release()); + } + return contexts; + } + + private static AcLoadFlowResult run(AcLoadFlowContext context) { + if (context.getNetwork().isValid() && context.isNetworkUpdated()) { + AcLoadFlowResult result = new AcloadFlowEngine(context) + .run(); + context.setNetworkUpdated(false); + return result; + } + return AcLoadFlowResult.createNoCalculationResult(context.getNetwork()); + } + + public List run() { + NetworkCache.Entry entry = NetworkCache.INSTANCE.get(network, parameters); + List contexts = entry.getContexts(); + if (contexts == null) { + contexts = initContexts(entry); + } + return contexts.stream() + .map(AcLoadFlowFromCache::run) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/powsybl/openloadflow/NetworkCache.java b/src/main/java/com/powsybl/openloadflow/NetworkCache.java new file mode 100644 index 0000000000..0c1939df14 --- /dev/null +++ b/src/main/java/com/powsybl/openloadflow/NetworkCache.java @@ -0,0 +1,318 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.openloadflow; + +import com.powsybl.iidm.network.*; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.openloadflow.ac.nr.NewtonRaphsonStatus; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowContext; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowResult; +import com.powsybl.openloadflow.network.LfBus; +import com.powsybl.openloadflow.network.LfNetwork; +import com.powsybl.openloadflow.network.VoltageControl; +import com.powsybl.openloadflow.network.impl.LfNetworkList; +import com.powsybl.openloadflow.network.util.PreviousValueVoltageInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.ref.WeakReference; +import java.util.*; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author Geoffroy Jamgotchian + */ +public enum NetworkCache { + INSTANCE; + + private static final Logger LOGGER = LoggerFactory.getLogger(NetworkCache.class); + + public static class Entry extends DefaultNetworkListener { + + private final WeakReference networkRef; + + private final String variantId; + + private final LoadFlowParameters parameters; + + private List contexts; + + private LfNetworkList.VariantCleaner variantCleaner; + + public Entry(Network network, LoadFlowParameters parameters) { + Objects.requireNonNull(network); + this.networkRef = new WeakReference<>(network); + this.variantId = network.getVariantManager().getWorkingVariantId(); + this.parameters = Objects.requireNonNull(parameters); + } + + public WeakReference getNetworkRef() { + return networkRef; + } + + public String getVariantId() { + return variantId; + } + + public List getContexts() { + return contexts; + } + + public void setContexts(List contexts) { + this.contexts = contexts; + } + + public void setVariantCleaner(LfNetworkList.VariantCleaner variantCleaner) { + this.variantCleaner = variantCleaner; + } + + public LoadFlowParameters getParameters() { + return parameters; + } + + private void reset() { + if (contexts != null) { + for (AcLoadFlowContext context : contexts) { + context.close(); + } + contexts = null; + } + } + + private void onStructureChange() { + // too difficult to update LfNetwork incrementally + reset(); + } + + @Override + public void onCreation(Identifiable identifiable) { + onStructureChange(); + } + + @Override + public void afterRemoval(String s) { + onStructureChange(); + } + + private static Bus getBus(Injection injection, AcLoadFlowContext context) { + return context.getParameters().getNetworkParameters().isBreakers() + ? injection.getTerminal().getBusBreakerView().getBus() + : injection.getTerminal().getBusView().getBus(); + } + + private boolean onGeneratorUpdate(Generator generator, String attribute, Object oldValue, Object newValue) { + boolean found = false; + for (AcLoadFlowContext context : contexts) { + Bus bus = getBus(generator, context); + if (bus != null) { + LfNetwork lfNetwork = context.getNetwork(); + LfBus lfBus = lfNetwork.getBusById(bus.getId()); + if (lfBus != null) { + if (attribute.equals("targetV")) { + double valueShift = (double) newValue - (double) oldValue; + VoltageControl voltageControl = lfBus.getVoltageControl().orElseThrow(); + double newTargetV = voltageControl.getTargetValue() + valueShift / lfBus.getNominalV(); + voltageControl.setTargetValue(newTargetV); + context.setNetworkUpdated(true); + found = true; + break; + } else { + throw new IllegalStateException("Unsupported generator attribute: " + attribute); + } + } + } + } + if (!found) { + LOGGER.warn("Cannot update {} of generator '{}'", attribute, generator.getId()); + } + return found; + } + + @Override + public void onUpdate(Identifiable identifiable, String attribute, String variantId, Object oldValue, Object newValue) { + if (contexts == null) { + return; + } + boolean done = false; + switch (attribute) { + case "v": + case "angle": + case "p": + case "q": + case "p1": + case "q1": + case "p2": + case "q2": + // ignore because it is related to state update and won't affect LF calculation + done = true; + break; + + default: + if (identifiable.getType() == IdentifiableType.GENERATOR) { + Generator generator = (Generator) identifiable; + if (attribute.equals("targetV") + && onGeneratorUpdate(generator, attribute, oldValue, newValue)) { + done = true; + } + } + break; + } + + if (!done) { + reset(); + } + } + + private void onPropertyChange() { + // nothing to do there could not have any impact on LF calculation + } + + @Override + public void onElementAdded(Identifiable identifiable, String attribute, Object newValue) { + onPropertyChange(); + } + + @Override + public void onElementReplaced(Identifiable identifiable, String attribute, Object oldValue, Object newValue) { + onPropertyChange(); + } + + @Override + public void onElementRemoved(Identifiable identifiable, String attribute, Object oldValue) { + onPropertyChange(); + } + + private void onVariantChange() { + // we reset + // TODO to study later if we can do better + reset(); + } + + @Override + public void onVariantCreated(String sourceVariantId, String targetVariantId) { + onVariantChange(); + } + + @Override + public void onVariantOverwritten(String sourceVariantId, String targetVariantId) { + onVariantChange(); + } + + @Override + public void onVariantRemoved(String variantId) { + onVariantChange(); + } + + public void close() { + reset(); + if (variantCleaner != null) { + variantCleaner.clean(); + variantCleaner = null; + } + } + } + + private final List entries = new ArrayList<>(); + + private final Lock lock = new ReentrantLock(); + + private void evictDeadEntries() { + Iterator it = entries.iterator(); + while (it.hasNext()) { + Entry entry = it.next(); + if (entry.getNetworkRef().get() == null) { + // release all resources + entry.close(); + it.remove(); + LOGGER.info("Dead network removed from cache ({} remains)", entries.size()); + } + } + } + + public int getEntryCount() { + lock.lock(); + try { + evictDeadEntries(); + return entries.size(); + } finally { + lock.unlock(); + } + } + + public Optional findEntry(Network network) { + String variantId = network.getVariantManager().getWorkingVariantId(); + return entries.stream() + .filter(e -> e.getNetworkRef().get() == network && e.getVariantId().equals(variantId)) + .findFirst(); + } + + public Entry get(Network network, LoadFlowParameters parameters) { + Objects.requireNonNull(network); + Objects.requireNonNull(parameters); + + Entry entry; + lock.lock(); + try { + evictDeadEntries(); + + entry = findEntry(network).orElse(null); + + // invalid cache if parameters have changed + // TODO to refine later by comparing in detail parameters that have changed + if (entry != null && !OpenLoadFlowParameters.equals(parameters, entry.getParameters())) { + // release all resources + entry.close(); + entries.remove(entry); + entry = null; + LOGGER.info("Network cache evicted because of parameters change"); + } + + if (entry == null) { + entry = new Entry(network, OpenLoadFlowParameters.clone(parameters)); + entries.add(entry); + network.addListener(entry); + + LOGGER.info("Network cache created for network '{}' and variant '{}'", + network.getId(), network.getVariantManager().getWorkingVariantId()); + + return entry; + } + } finally { + lock.unlock(); + } + + // restart from previous state + if (entry.getContexts() != null) { + LOGGER.info("Network cache reused for network '{}' and variant '{}'", + network.getId(), network.getVariantManager().getWorkingVariantId()); + + for (AcLoadFlowContext context : entry.getContexts()) { + AcLoadFlowResult result = context.getResult(); + if (result != null && result.getNewtonRaphsonStatus() == NewtonRaphsonStatus.CONVERGED) { + context.getParameters().setVoltageInitializer(new PreviousValueVoltageInitializer()); + } + } + } else { + LOGGER.info("Network cache cannot be reused for network '{}' because invalided", network.getId()); + } + + return entry; + } + + public void clear() { + lock.lock(); + try { + for (var entry : entries) { + entry.close(); + } + entries.clear(); + } finally { + lock.unlock(); + } + } +} diff --git a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java index 3aa2b7a846..8c72b3a41c 100644 --- a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java +++ b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java @@ -60,6 +60,8 @@ public class OpenLoadFlowParameters extends AbstractExtension SPECIFIC_PARAMETERS_NAMES = List.of(SLACK_BUS_SELECTION_PARAM_NAME, SLACK_BUSES_IDS_PARAM_NAME, LOW_IMPEDANCE_BRANCH_MODE_PARAM_NAME, @@ -122,7 +126,8 @@ public class OpenLoadFlowParameters extends AbstractExtension properties) { .ifPresent(prop -> this.setReactiveRangeCheckMode(ReactiveRangeCheckMode.valueOf(prop))); Optional.ofNullable(properties.get(LOW_IMPEDANCE_THRESHOLD_NAME)) .ifPresent(prop -> this.setLowImpedanceThreshold(Double.parseDouble(prop))); + Optional.ofNullable(properties.get(NETWORK_CACHE_ENABLED_NAME)) + .ifPresent(prop -> this.setNetworkCacheEnabled(Boolean.parseBoolean(prop))); return this; } @@ -506,6 +525,7 @@ public String toString() { ", maxRealisticVoltage=" + maxRealisticVoltage + ", reactiveRangeCheckMode=" + reactiveRangeCheckMode + ", lowImpedanceThreshold=" + lowImpedanceThreshold + + ", networkCacheEnabled=" + networkCacheEnabled + ')'; } @@ -571,6 +591,7 @@ public static void logAc(LoadFlowParameters parameters, OpenLoadFlowParameters p LOGGER.info("Min realistic voltage: {}", parametersExt.getMinRealisticVoltage()); LOGGER.info("Max realistic voltage: {}", parametersExt.getMaxRealisticVoltage()); LOGGER.info("Reactive range check mode: {}", parametersExt.getReactiveRangeCheckMode()); + LOGGER.info("Network cache enabled: {}", parametersExt.isNetworkCacheEnabled()); } static VoltageInitializer getVoltageInitializer(LoadFlowParameters parameters, LfNetworkParameters networkParameters, MatrixFactory matrixFactory) { @@ -730,4 +751,114 @@ public static DcLoadFlowParameters createDcParameters(LoadFlowParameters paramet parameters.getBalanceType(), true); } + + public static boolean equals(LoadFlowParameters parameters1, LoadFlowParameters parameters2) { + Objects.requireNonNull(parameters1); + Objects.requireNonNull(parameters2); + boolean equals = parameters1.getVoltageInitMode() == parameters2.getVoltageInitMode() && + parameters1.isTransformerVoltageControlOn() == parameters2.isTransformerVoltageControlOn() && + parameters1.isNoGeneratorReactiveLimits() == parameters2.isNoGeneratorReactiveLimits() && + parameters1.isPhaseShifterRegulationOn() == parameters2.isPhaseShifterRegulationOn() && + parameters1.isTwtSplitShuntAdmittance() == parameters2.isTwtSplitShuntAdmittance() && + parameters1.isShuntCompensatorVoltageControlOn() == parameters2.isShuntCompensatorVoltageControlOn() && + parameters1.isReadSlackBus() == parameters2.isReadSlackBus() && + parameters1.isWriteSlackBus() == parameters2.isWriteSlackBus() && + parameters1.isDc() == parameters2.isDc() && + parameters1.isDistributedSlack() == parameters2.isDistributedSlack() && + parameters1.getBalanceType() == parameters2.getBalanceType() && + parameters1.isDcUseTransformerRatio() == parameters2.isDcUseTransformerRatio() && + parameters1.getCountriesToBalance().equals(parameters2.getCountriesToBalance()) && + parameters1.getConnectedComponentMode() == parameters2.getConnectedComponentMode() && + parameters1.isHvdcAcEmulation() == parameters2.isHvdcAcEmulation(); + if (!equals) { + return false; + } + + OpenLoadFlowParameters extension1 = parameters1.getExtension(OpenLoadFlowParameters.class); + OpenLoadFlowParameters extension2 = parameters2.getExtension(OpenLoadFlowParameters.class); + if (extension1 == null && extension2 == null) { + return true; + } + if (extension1 == null) { + return false; + } + if (extension2 == null) { + return false; + } + + return extension1.getSlackBusSelectionMode() == extension2.getSlackBusSelectionMode() && + extension1.getSlackBusesIds().equals(extension2.getSlackBusesIds()) && + extension1.isThrowsExceptionInCaseOfSlackDistributionFailure() == extension2.isThrowsExceptionInCaseOfSlackDistributionFailure() && + extension1.hasVoltageRemoteControl() == extension2.hasVoltageRemoteControl() && + extension1.getLowImpedanceBranchMode() == extension2.getLowImpedanceBranchMode() && + extension1.isLoadPowerFactorConstant() == extension2.isLoadPowerFactorConstant() && + extension1.getPlausibleActivePowerLimit() == extension2.getPlausibleActivePowerLimit() && + extension1.getSlackBusPMaxMismatch() == extension2.getSlackBusPMaxMismatch() && + extension1.isVoltagePerReactivePowerControl() == extension2.isVoltagePerReactivePowerControl() && + extension1.hasReactivePowerRemoteControl() == extension2.hasReactivePowerRemoteControl() && + extension1.getMaxIteration() == extension2.getMaxIteration() && + extension1.getNewtonRaphsonConvEpsPerEq() == extension2.getNewtonRaphsonConvEpsPerEq() && + extension1.getVoltageInitModeOverride() == extension2.getVoltageInitModeOverride() && + extension1.getTransformerVoltageControlMode() == extension2.getTransformerVoltageControlMode() && + extension1.getDcPowerFactor() == extension2.getDcPowerFactor() && + extension1.getMinPlausibleTargetVoltage() == extension2.getMinPlausibleTargetVoltage() && + extension1.getMaxPlausibleTargetVoltage() == extension2.getMaxPlausibleTargetVoltage() && + extension1.getMinRealisticVoltage() == extension2.getMinRealisticVoltage() && + extension1.getMaxRealisticVoltage() == extension2.getMaxRealisticVoltage() && + extension1.getReactiveRangeCheckMode() == extension2.getReactiveRangeCheckMode() && + extension1.getLowImpedanceThreshold() == extension2.getLowImpedanceThreshold() && + extension1.isNetworkCacheEnabled() == extension2.isNetworkCacheEnabled(); + } + + public static LoadFlowParameters clone(LoadFlowParameters parameters) { + Objects.requireNonNull(parameters); + LoadFlowParameters parameters2 = new LoadFlowParameters(parameters.getVoltageInitMode(), + parameters.isTransformerVoltageControlOn(), + parameters.isNoGeneratorReactiveLimits(), + parameters.isPhaseShifterRegulationOn(), + parameters.isTwtSplitShuntAdmittance(), + parameters.isShuntCompensatorVoltageControlOn(), + parameters.isReadSlackBus(), + parameters.isWriteSlackBus(), + parameters.isDc(), + parameters.isDistributedSlack(), + parameters.getBalanceType(), + parameters.isDcUseTransformerRatio(), + new HashSet<>(parameters.getCountriesToBalance()), + parameters.getConnectedComponentMode(), + parameters.isHvdcAcEmulation()); + + OpenLoadFlowParameters extension = parameters.getExtension(OpenLoadFlowParameters.class); + if (extension != null) { + OpenLoadFlowParameters extension2 = new OpenLoadFlowParameters() + .setSlackBusSelectionMode(extension.getSlackBusSelectionMode()) + .setSlackBusesIds(new ArrayList<>(extension.getSlackBusesIds())) + .setThrowsExceptionInCaseOfSlackDistributionFailure(extension.isThrowsExceptionInCaseOfSlackDistributionFailure()) + .setVoltageRemoteControl(extension.hasVoltageRemoteControl()) + .setLowImpedanceBranchMode(extension.getLowImpedanceBranchMode()) + .setLoadPowerFactorConstant(extension.isLoadPowerFactorConstant()) + .setPlausibleActivePowerLimit(extension.getPlausibleActivePowerLimit()) + .setSlackBusPMaxMismatch(extension.getSlackBusPMaxMismatch()) + .setVoltagePerReactivePowerControl(extension.isVoltagePerReactivePowerControl()) + .setReactivePowerRemoteControl(extension.hasReactivePowerRemoteControl()) + .setMaxIteration(extension.getMaxIteration()) + .setNewtonRaphsonConvEpsPerEq(extension.getNewtonRaphsonConvEpsPerEq()) + .setVoltageInitModeOverride(extension.getVoltageInitModeOverride()) + .setTransformerVoltageControlMode(extension.getTransformerVoltageControlMode()) + .setDcPowerFactor(extension.getDcPowerFactor()) + .setMinPlausibleTargetVoltage(extension.getMinPlausibleTargetVoltage()) + .setMaxPlausibleTargetVoltage(extension.getMaxPlausibleTargetVoltage()) + .setMinRealisticVoltage(extension.getMinRealisticVoltage()) + .setMaxRealisticVoltage(extension.getMaxRealisticVoltage()) + .setReactiveRangeCheckMode(extension.getReactiveRangeCheckMode()) + .setLowImpedanceThreshold(extension.getLowImpedanceThreshold()) + .setNetworkCacheEnabled(extension.isNetworkCacheEnabled()); + if (extension2 != null) { + parameters2.addExtension(OpenLoadFlowParameters.class, extension2); + } + } + + return parameters2; + } } + diff --git a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java index af647d5446..111ed87d92 100644 --- a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java +++ b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java @@ -22,10 +22,7 @@ import com.powsybl.math.matrix.MatrixFactory; import com.powsybl.math.matrix.SparseMatrixFactory; import com.powsybl.openloadflow.ac.nr.NewtonRaphsonStatus; -import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters; -import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowResult; -import com.powsybl.openloadflow.ac.outerloop.AcloadFlowEngine; -import com.powsybl.openloadflow.ac.outerloop.OuterLoop; +import com.powsybl.openloadflow.ac.outerloop.*; import com.powsybl.openloadflow.dc.DcLoadFlowEngine; import com.powsybl.openloadflow.dc.DcLoadFlowResult; import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory; @@ -101,7 +98,13 @@ private LoadFlowResult runAc(Network network, LoadFlowParameters parameters, Rep LOGGER.info("Outer loops: {}", acParameters.getOuterLoops().stream().map(OuterLoop::getType).collect(Collectors.toList())); } - List results = AcloadFlowEngine.run(network, new LfNetworkLoaderImpl(), acParameters, reporter); + List results; + if (parametersExt.isNetworkCacheEnabled()) { + results = new AcLoadFlowFromCache(network, parameters, acParameters, reporter) + .run(); + } else { + results = AcloadFlowEngine.run(network, new LfNetworkLoaderImpl(), acParameters, reporter); + } Networks.resetState(network); diff --git a/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcLoadFlowContext.java b/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcLoadFlowContext.java index b289578ffe..7fb9d40207 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcLoadFlowContext.java +++ b/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcLoadFlowContext.java @@ -24,6 +24,10 @@ public class AcLoadFlowContext extends AbstractLoadFlowContext equationVector; + private AcLoadFlowResult result; + + private boolean networkUpdated = true; + public AcLoadFlowContext(LfNetwork network, AcLoadFlowParameters parameters) { super(network, parameters); } @@ -52,6 +56,22 @@ public EquationVector getEquationVector() { return equationVector; } + public AcLoadFlowResult getResult() { + return result; + } + + public void setResult(AcLoadFlowResult result) { + this.result = result; + } + + public boolean isNetworkUpdated() { + return networkUpdated; + } + + public void setNetworkUpdated(boolean networkUpdated) { + this.networkUpdated = networkUpdated; + } + @Override public void close() { super.close(); diff --git a/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcLoadFlowResult.java b/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcLoadFlowResult.java index 92308d9f2d..aded06a7d0 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcLoadFlowResult.java +++ b/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcLoadFlowResult.java @@ -16,6 +16,10 @@ */ public class AcLoadFlowResult extends AbstractLoadFlowResult { + public static AcLoadFlowResult createNoCalculationResult(LfNetwork network) { + return new AcLoadFlowResult(network, 0, 0, NewtonRaphsonStatus.NO_CALCULATION, Double.NaN, Double.NaN); + } + private final int outerLoopIterations; private final int newtonRaphsonIterations; diff --git a/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcloadFlowEngine.java b/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcloadFlowEngine.java index ebbcb83ee0..aff1c920cf 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcloadFlowEngine.java +++ b/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcloadFlowEngine.java @@ -159,6 +159,8 @@ public AcLoadFlowResult run() { Reports.reportAcLfComplete(context.getNetwork().getReporter(), result.getNewtonRaphsonStatus().name()); + context.setResult(result); + return result; } @@ -172,7 +174,7 @@ public static List run(T network, LfNetworkLoader netwo .run(); } } - return new AcLoadFlowResult(n, 0, 0, NewtonRaphsonStatus.NO_CALCULATION, Double.NaN, Double.NaN); + return AcLoadFlowResult.createNoCalculationResult(n); }) .collect(Collectors.toList()); } diff --git a/src/main/java/com/powsybl/openloadflow/equations/TargetVector.java b/src/main/java/com/powsybl/openloadflow/equations/TargetVector.java index 2e6fc547fb..d4885826e8 100644 --- a/src/main/java/com/powsybl/openloadflow/equations/TargetVector.java +++ b/src/main/java/com/powsybl/openloadflow/equations/TargetVector.java @@ -28,6 +28,11 @@ public interface Initializer & Quantity, E extends Enum & Q private final LfNetworkListener networkListener = new AbstractLfNetworkListener() { + @Override + public void onVoltageControlTargetChange(VoltageControl control, double newTargetVoltage) { + invalidateValues(); + } + @Override public void onLoadActivePowerTargetChange(LfBus bus, double oldLoadTargetP, double newLoadTargetP) { invalidateValues(); diff --git a/src/main/java/com/powsybl/openloadflow/network/AbstractLfNetworkListener.java b/src/main/java/com/powsybl/openloadflow/network/AbstractLfNetworkListener.java index b003c576cc..b6d4f45abb 100644 --- a/src/main/java/com/powsybl/openloadflow/network/AbstractLfNetworkListener.java +++ b/src/main/java/com/powsybl/openloadflow/network/AbstractLfNetworkListener.java @@ -16,6 +16,11 @@ public void onVoltageControlChange(LfBus controllerBus, boolean newVoltageContro // empty } + @Override + public void onVoltageControlTargetChange(VoltageControl control, double newTargetVoltage) { + // empty + } + @Override public void onTransformerPhaseControlChange(LfBranch branch, boolean phaseControlEnabled) { // empty diff --git a/src/main/java/com/powsybl/openloadflow/network/LfNetworkListener.java b/src/main/java/com/powsybl/openloadflow/network/LfNetworkListener.java index 1abb874ba4..4dfba50f74 100644 --- a/src/main/java/com/powsybl/openloadflow/network/LfNetworkListener.java +++ b/src/main/java/com/powsybl/openloadflow/network/LfNetworkListener.java @@ -13,6 +13,8 @@ public interface LfNetworkListener { void onVoltageControlChange(LfBus controllerBus, boolean newVoltageControllerEnabled); + void onVoltageControlTargetChange(VoltageControl control, double newTargetVoltage); + void onTransformerPhaseControlChange(LfBranch branch, boolean phaseControlEnabled); void onTransformerVoltageControlChange(LfBranch controllerBranch, boolean newVoltageControllerEnabled); diff --git a/src/main/java/com/powsybl/openloadflow/network/VoltageControl.java b/src/main/java/com/powsybl/openloadflow/network/VoltageControl.java index f336dc3caf..82edf90512 100644 --- a/src/main/java/com/powsybl/openloadflow/network/VoltageControl.java +++ b/src/main/java/com/powsybl/openloadflow/network/VoltageControl.java @@ -24,7 +24,7 @@ public class VoltageControl { private final Set controllers; - private final double targetValue; + private double targetValue; public VoltageControl(LfBus controlled, double targetValue) { this.controlled = controlled; @@ -36,6 +36,13 @@ public double getTargetValue() { return targetValue; } + public void setTargetValue(double targetValue) { + if (targetValue != this.targetValue) { + this.targetValue = targetValue; + controlled.getNetwork().getListeners().forEach(l -> l.onVoltageControlTargetChange(this, targetValue)); + } + } + public LfBus getControlledBus() { return controlled; } diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBus.java b/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBus.java index 28fe7c627f..48575ad00d 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBus.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBus.java @@ -62,7 +62,7 @@ public abstract class AbstractLfBus extends AbstractElement implements LfBus { protected boolean ensurePowerFactorConstantByLoad = false; - protected final List lccCss = new ArrayList<>(); + protected final List> lccCsRefs = new ArrayList<>(); protected final List branches = new ArrayList<>(); @@ -211,7 +211,7 @@ void addLoad(Load load) { void addLccConverterStation(LccConverterStation lccCs) { // note that LCC converter station are out of the slack distribution. - lccCss.add(lccCs); + lccCsRefs.add(new Ref<>(lccCs)); double targetP = HvdcConverterStations.getConverterStationTargetP(lccCs); loadTargetP += targetP; initialLoadTargetP += targetP; @@ -472,7 +472,8 @@ public void updateState(boolean reactiveLimits, boolean writeSlackBus, boolean d lfAggregatedLoads.updateState(getLoadTargetP() - getInitialLoadTargetP(), loadPowerFactorConstant); // update lcc converter station power - for (LccConverterStation lccCs : lccCss) { + for (Ref lccCsRef : lccCsRefs) { + LccConverterStation lccCs = lccCsRef.get(); double pCs = HvdcConverterStations.getConverterStationTargetP(lccCs); // A LCC station has active losses. double qCs = HvdcConverterStations.getLccConverterStationLoadTargetQ(lccCs); // A LCC station always consumes reactive power. lccCs.getTerminal() diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfAggregatedLoadsImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfAggregatedLoadsImpl.java index ac516fe385..d95c1b8714 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfAggregatedLoadsImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfAggregatedLoadsImpl.java @@ -6,7 +6,6 @@ */ package com.powsybl.openloadflow.network.impl; -import com.powsybl.iidm.network.Identifiable; import com.powsybl.iidm.network.Load; import com.powsybl.iidm.network.extensions.LoadDetail; import com.powsybl.openloadflow.network.AbstractPropertyBag; @@ -22,7 +21,7 @@ */ class LfAggregatedLoadsImpl extends AbstractPropertyBag implements LfAggregatedLoads { - private final List loads = new ArrayList<>(); + private final List> loadsRefs = new ArrayList<>(); private double[] participationFactors; @@ -38,11 +37,11 @@ class LfAggregatedLoadsImpl extends AbstractPropertyBag implements LfAggregatedL @Override public List getOriginalIds() { - return loads.stream().map(Identifiable::getId).collect(Collectors.toList()); + return loadsRefs.stream().map(r -> r.get().getId()).collect(Collectors.toList()); } void add(Load load) { - loads.add(load); + loadsRefs.add(new Ref<>(load)); initialized = false; } @@ -62,10 +61,10 @@ private void init() { return; } - participationFactors = new double[loads.size()]; + participationFactors = new double[loadsRefs.size()]; absVariableLoadTargetP = 0; - for (int i = 0; i < loads.size(); i++) { - Load load = loads.get(i); + for (int i = 0; i < loadsRefs.size(); i++) { + Load load = loadsRefs.get(i).get(); double value; if (distributedOnConformLoad) { value = load.getExtension(LoadDetail.class) == null ? 0. : Math.abs(load.getExtension(LoadDetail.class).getVariableActivePower()); @@ -87,13 +86,13 @@ private void init() { @Override public double getLoadCount() { - return loads.size(); + return loadsRefs.size(); } void updateState(double diffLoadTargetP, boolean loadPowerFactorConstant) { init(); - for (int i = 0; i < loads.size(); i++) { - Load load = loads.get(i); + for (int i = 0; i < loadsRefs.size(); i++) { + Load load = loadsRefs.get(i).get(); double updatedP0 = (load.getP0() / PerUnit.SB + diffLoadTargetP * participationFactors[i]) * PerUnit.SB; double updatedQ0 = loadPowerFactorConstant ? getPowerFactor(load) * updatedP0 : load.getQ0(); load.getTerminal().setP(updatedP0); @@ -105,8 +104,8 @@ void updateState(double diffLoadTargetP, boolean loadPowerFactorConstant) { public double getLoadTargetQ(double diffLoadTargetP) { init(); double newLoadTargetQ = 0; - for (int i = 0; i < loads.size(); i++) { - Load load = loads.get(i); + for (int i = 0; i < loadsRefs.size(); i++) { + Load load = loadsRefs.get(i).get(); double updatedP0 = load.getP0() / PerUnit.SB + diffLoadTargetP * participationFactors[i]; newLoadTargetQ += getPowerFactor(load) * updatedP0; } diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfBatteryImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfBatteryImpl.java index e2436d99c7..d429a0e906 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfBatteryImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfBatteryImpl.java @@ -20,7 +20,7 @@ */ public final class LfBatteryImpl extends AbstractLfGenerator { - private final Battery battery; + private final Ref batteryRef; private boolean participating; @@ -28,7 +28,7 @@ public final class LfBatteryImpl extends AbstractLfGenerator { private LfBatteryImpl(Battery battery, LfNetwork network, double plausibleActivePowerLimit, LfNetworkLoadingReport report) { super(network, battery.getTargetP()); - this.battery = battery; + this.batteryRef = new Ref<>(battery); participating = true; droop = DEFAULT_DROOP; // get participation factor from extension @@ -51,29 +51,33 @@ public static LfBatteryImpl create(Battery battery, LfNetwork network, double pl return new LfBatteryImpl(battery, network, plausibleActivePowerLimit, report); } + private Battery getBattery() { + return batteryRef.get(); + } + @Override public String getId() { - return battery.getId(); + return getBattery().getId(); } @Override public double getTargetQ() { - return battery.getTargetQ() / PerUnit.SB; + return getBattery().getTargetQ() / PerUnit.SB; } @Override public double getMinP() { - return battery.getMinP() / PerUnit.SB; + return getBattery().getMinP() / PerUnit.SB; } @Override public double getMaxP() { - return battery.getMaxP() / PerUnit.SB; + return getBattery().getMaxP() / PerUnit.SB; } @Override protected Optional getReactiveLimits() { - return Optional.of(battery.getReactiveLimits()); + return Optional.of(getBattery().getReactiveLimits()); } @Override @@ -93,6 +97,7 @@ public double getDroop() { @Override public void updateState() { + var battery = getBattery(); battery.getTerminal() .setP(-targetP) .setQ(-battery.getTargetQ()); diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfBranchImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfBranchImpl.java index 7a27e9fce4..ab1ee609c5 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfBranchImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfBranchImpl.java @@ -21,11 +21,11 @@ */ public class LfBranchImpl extends AbstractImpedantLfBranch { - private final Branch branch; + private final Ref> branchRef; protected LfBranchImpl(LfNetwork network, LfBus bus1, LfBus bus2, PiModel piModel, Branch branch) { super(network, bus1, bus2, piModel); - this.branch = branch; + this.branchRef = new Ref<>(branch); } private static LfBranchImpl createLine(Line line, LfNetwork network, LfBus bus1, LfBus bus2, double zb) { @@ -102,24 +102,30 @@ public static LfBranchImpl create(Branch branch, LfNetwork network, LfBus bus } } + private Branch getBranch() { + return branchRef.get(); + } + @Override public String getId() { - return branch.getId(); + return getBranch().getId(); } @Override public BranchType getBranchType() { - return branch instanceof Line ? BranchType.LINE : BranchType.TRANSFO_2; + return getBranch() instanceof Line ? BranchType.LINE : BranchType.TRANSFO_2; } @Override public boolean hasPhaseControlCapability() { + var branch = getBranch(); return branch.getType() == IdentifiableType.TWO_WINDINGS_TRANSFORMER && ((TwoWindingsTransformer) branch).getPhaseTapChanger() != null; } @Override public BranchResult createBranchResult(double preContingencyBranchP1, double preContingencyBranchOfContingencyP1, boolean createExtension) { + var branch = getBranch(); double flowTransfer = Double.NaN; if (!Double.isNaN(preContingencyBranchP1) && !Double.isNaN(preContingencyBranchOfContingencyP1)) { flowTransfer = (p1.eval() * PerUnit.SB - preContingencyBranchP1) / preContingencyBranchOfContingencyP1; @@ -136,6 +142,7 @@ public BranchResult createBranchResult(double preContingencyBranchP1, double pre @Override public List getLimits1(final LimitType type) { + var branch = getBranch(); switch (type) { case ACTIVE_POWER: return getLimits1(type, branch.getActivePowerLimits1().orElse(null)); @@ -151,6 +158,7 @@ public List getLimits1(final LimitType type) { @Override public List getLimits2(final LimitType type) { + var branch = getBranch(); switch (type) { case ACTIVE_POWER: return getLimits2(type, branch.getActivePowerLimits2().orElse(null)); @@ -166,6 +174,8 @@ public List getLimits2(final LimitType type) { @Override public void updateState(boolean phaseShifterRegulationOn, boolean isTransformerVoltageControlOn, boolean dc) { + var branch = getBranch(); + updateFlows(p1.eval(), q1.eval(), p2.eval(), q2.eval()); if (phaseShifterRegulationOn && isPhaseController()) { @@ -188,6 +198,7 @@ public void updateState(boolean phaseShifterRegulationOn, boolean isTransformerV @Override public void updateFlows(double p1, double q1, double p2, double q2) { + var branch = getBranch(); branch.getTerminal1().setP(p1 * PerUnit.SB) .setQ(q1 * PerUnit.SB); branch.getTerminal2().setP(p2 * PerUnit.SB) diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfBusImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfBusImpl.java index 557c90ca2c..1a782aae45 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfBusImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfBusImpl.java @@ -20,7 +20,7 @@ */ public class LfBusImpl extends AbstractLfBus { - private final Bus bus; + private final Ref busRef; private final double nominalV; @@ -35,7 +35,7 @@ public class LfBusImpl extends AbstractLfBus { protected LfBusImpl(Bus bus, LfNetwork network, double v, double angle, boolean distributedOnConformLoad, boolean participating, boolean breakers) { super(network, v, angle, distributedOnConformLoad); - this.bus = bus; + this.busRef = new Ref<>(bus); nominalV = bus.getVoltageLevel().getNominalV(); lowVoltageLimit = bus.getVoltageLevel().getLowVoltageLimit(); highVoltageLimit = bus.getVoltageLevel().getHighVoltageLimit(); @@ -49,14 +49,18 @@ public static LfBusImpl create(Bus bus, LfNetwork network, boolean distributedOn return new LfBusImpl(bus, network, bus.getV(), bus.getAngle(), distributedOnConformLoad, participating, breakers); } + private Bus getBus() { + return busRef.get(); + } + @Override public String getId() { - return bus.getId(); + return getBus().getId(); } @Override public String getVoltageLevelId() { - return bus.getVoltageLevel().getId(); + return getBus().getVoltageLevel().getId(); } @Override @@ -81,6 +85,7 @@ public double getHighVoltageLimit() { @Override public void updateState(boolean reactiveLimits, boolean writeSlackBus, boolean distributedOnConformLoad, boolean loadPowerFactorConstant) { + var bus = getBus(); bus.setV(v).setAngle(angle); // update slack bus @@ -98,6 +103,7 @@ public boolean isParticipating() { @Override public List createBusResults() { + var bus = getBus(); if (breakers) { return List.of(new BusResult(getVoltageLevelId(), bus.getId(), v, getAngle())); } else { diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBranch.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBranch.java index 6539eec6d9..637257f9e9 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBranch.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBranch.java @@ -9,7 +9,10 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.DanglingLine; import com.powsybl.iidm.network.LimitType; -import com.powsybl.openloadflow.network.*; +import com.powsybl.openloadflow.network.LfBus; +import com.powsybl.openloadflow.network.LfNetwork; +import com.powsybl.openloadflow.network.PiModel; +import com.powsybl.openloadflow.network.SimplePiModel; import com.powsybl.openloadflow.util.PerUnit; import com.powsybl.security.results.BranchResult; @@ -21,11 +24,11 @@ */ public class LfDanglingLineBranch extends AbstractImpedantLfBranch { - private final DanglingLine danglingLine; + private final Ref danglingLineRef; protected LfDanglingLineBranch(LfNetwork network, LfBus bus1, LfBus bus2, PiModel piModel, DanglingLine danglingLine) { super(network, bus1, bus2, piModel); - this.danglingLine = danglingLine; + this.danglingLineRef = new Ref<>(danglingLine); } public static LfDanglingLineBranch create(DanglingLine danglingLine, LfNetwork network, LfBus bus1, LfBus bus2) { @@ -44,9 +47,13 @@ public static LfDanglingLineBranch create(DanglingLine danglingLine, LfNetwork n return new LfDanglingLineBranch(network, bus1, bus2, piModel, danglingLine); } + private DanglingLine getDanglingLine() { + return danglingLineRef.get(); + } + @Override public String getId() { - return danglingLine.getId(); + return getDanglingLine().getId(); } @Override @@ -66,6 +73,7 @@ public BranchResult createBranchResult(double preContingencyBranchP1, double pre @Override public List getLimits1(final LimitType type) { + var danglingLine = getDanglingLine(); switch (type) { case ACTIVE_POWER: return getLimits1(type, danglingLine.getActivePowerLimits().orElse(null)); @@ -87,7 +95,7 @@ public void updateState(boolean phaseShifterRegulationOn, boolean isTransformerV @Override public void updateFlows(double p1, double q1, double p2, double q2) { // Network side is always on side 1. - danglingLine.getTerminal().setP(p1 * PerUnit.SB) + getDanglingLine().getTerminal().setP(p1 * PerUnit.SB) .setQ(q1 * PerUnit.SB); } } diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBus.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBus.java index 77b11369b4..e00f65f94e 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBus.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBus.java @@ -11,21 +11,20 @@ import com.powsybl.openloadflow.network.LfNetwork; import java.util.List; -import java.util.Objects; /** * @author Geoffroy Jamgotchian */ public class LfDanglingLineBus extends AbstractLfBus { - private final DanglingLine danglingLine; + private final Ref danglingLineRef; private final double nominalV; public LfDanglingLineBus(LfNetwork network, DanglingLine danglingLine, boolean reactiveLimits, LfNetworkLoadingReport report, double minPlausibleTargetVoltage, double maxPlausibleTargetVoltage, OpenLoadFlowParameters.ReactiveRangeCheckMode reactiveRangeCheckMode) { super(network, Networks.getPropertyV(danglingLine), Networks.getPropertyAngle(danglingLine), false); - this.danglingLine = Objects.requireNonNull(danglingLine); + this.danglingLineRef = new Ref<>(danglingLine); nominalV = danglingLine.getTerminal().getVoltageLevel().getNominalV(); loadTargetP += danglingLine.getP0(); loadTargetQ += danglingLine.getQ0(); @@ -35,23 +34,27 @@ public LfDanglingLineBus(LfNetwork network, DanglingLine danglingLine, boolean r } } + private DanglingLine getDanglingLine() { + return danglingLineRef.get(); + } + public static String getId(DanglingLine danglingLine) { return danglingLine.getId() + "_BUS"; } @Override public List getOriginalIds() { - return List.of(danglingLine.getId()); + return List.of(getDanglingLine().getId()); } @Override public String getId() { - return getId(danglingLine); + return getId(getDanglingLine()); } @Override public String getVoltageLevelId() { - return danglingLine.getTerminal().getVoltageLevel().getId(); + return getDanglingLine().getTerminal().getVoltageLevel().getId(); } @Override @@ -66,6 +69,7 @@ public double getNominalV() { @Override public void updateState(boolean reactiveLimits, boolean writeSlackBus, boolean distributedOnConformLoad, boolean loadPowerFactorConstant) { + var danglingLine = getDanglingLine(); Networks.setPropertyV(danglingLine, v); Networks.setPropertyAngle(danglingLine, angle); diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java index d14fddda81..a950f642b6 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java @@ -21,12 +21,12 @@ */ public class LfDanglingLineGenerator extends AbstractLfGenerator { - private final DanglingLine danglingLine; + private final Ref danglingLineRef; public LfDanglingLineGenerator(DanglingLine danglingLine, LfNetwork network, String controlledLfBusId, boolean reactiveLimits, LfNetworkLoadingReport report, double minPlausibleTargetVoltage, double maxPlausibleTargetVoltage, OpenLoadFlowParameters.ReactiveRangeCheckMode reactiveRangeCheckMode) { super(network, danglingLine.getGeneration().getTargetP()); - this.danglingLine = danglingLine; + this.danglingLineRef = new Ref<>(danglingLine); // local control only if (danglingLine.getGeneration().isVoltageRegulationOn() && checkVoltageControlConsistency(reactiveLimits, report, reactiveRangeCheckMode)) { @@ -40,14 +40,18 @@ public LfDanglingLineGenerator(DanglingLine danglingLine, LfNetwork network, Str } } + private DanglingLine getDanglingLine() { + return danglingLineRef.get(); + } + @Override public String getId() { - return danglingLine.getId() + "_GEN"; + return getDanglingLine().getId() + "_GEN"; } @Override public String getOriginalId() { - return danglingLine.getId(); + return getDanglingLine().getId(); } @Override @@ -57,22 +61,22 @@ public OptionalDouble getRemoteControlReactiveKey() { @Override public double getTargetQ() { - return danglingLine.getGeneration().getTargetQ() / PerUnit.SB; + return getDanglingLine().getGeneration().getTargetQ() / PerUnit.SB; } @Override public double getMinP() { - return danglingLine.getGeneration().getMinP() / PerUnit.SB; + return getDanglingLine().getGeneration().getMinP() / PerUnit.SB; } @Override public double getMaxP() { - return danglingLine.getGeneration().getMaxP() / PerUnit.SB; + return getDanglingLine().getGeneration().getMaxP() / PerUnit.SB; } @Override protected Optional getReactiveLimits() { - return Optional.ofNullable(danglingLine.getGeneration().getReactiveLimits()); + return Optional.ofNullable(getDanglingLine().getGeneration().getReactiveLimits()); } @Override diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfGeneratorImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfGeneratorImpl.java index dd0b911b44..453438e3e4 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfGeneratorImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfGeneratorImpl.java @@ -24,7 +24,7 @@ */ public final class LfGeneratorImpl extends AbstractLfGenerator { - private final Generator generator; + private final Ref generatorRef; private boolean participating; @@ -33,7 +33,7 @@ public final class LfGeneratorImpl extends AbstractLfGenerator { private LfGeneratorImpl(Generator generator, LfNetwork network, boolean breakers, double plausibleActivePowerLimit, boolean reactiveLimits, LfNetworkLoadingReport report, double minPlausibleTargetVoltage, double maxPlausibleTargetVoltage, OpenLoadFlowParameters.ReactiveRangeCheckMode reactiveRangeCheckMode) { super(network, generator.getTargetP()); - this.generator = generator; + this.generatorRef = new Ref<>(generator); participating = true; droop = DEFAULT_DROOP; // get participation factor from extension @@ -69,19 +69,23 @@ public static LfGeneratorImpl create(Generator generator, LfNetwork network, boo minPlausibleTargetVoltage, maxPlausibleTargetVoltage, reactiveRangeCheckMode); } + private Generator getGenerator() { + return generatorRef.get(); + } + @Override public String getId() { - return generator.getId(); + return getGenerator().getId(); } @Override public boolean isFictitious() { - return generator.isFictitious(); + return getGenerator().isFictitious(); } @Override public OptionalDouble getRemoteControlReactiveKey() { - CoordinatedReactiveControl coordinatedReactiveControl = generator.getExtension(CoordinatedReactiveControl.class); + CoordinatedReactiveControl coordinatedReactiveControl = getGenerator().getExtension(CoordinatedReactiveControl.class); if (coordinatedReactiveControl == null) { return OptionalDouble.empty(); } @@ -90,22 +94,22 @@ public OptionalDouble getRemoteControlReactiveKey() { @Override public double getTargetQ() { - return generator.getTargetQ() / PerUnit.SB; + return getGenerator().getTargetQ() / PerUnit.SB; } @Override public double getMinP() { - return generator.getMinP() / PerUnit.SB; + return getGenerator().getMinP() / PerUnit.SB; } @Override public double getMaxP() { - return generator.getMaxP() / PerUnit.SB; + return getGenerator().getMaxP() / PerUnit.SB; } @Override protected Optional getReactiveLimits() { - return Optional.of(generator.getReactiveLimits()); + return Optional.of(getGenerator().getReactiveLimits()); } @Override @@ -125,6 +129,7 @@ public double getDroop() { @Override public void updateState() { + var generator = getGenerator(); generator.getTerminal() .setP(-targetP) .setQ(Double.isNaN(calculatedQ) ? -generator.getTargetQ() : -calculatedQ); diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfLegBranch.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfLegBranch.java index 60bc9a6385..dcfd608b03 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfLegBranch.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfLegBranch.java @@ -7,10 +7,7 @@ package com.powsybl.openloadflow.network.impl; import com.powsybl.commons.PowsyblException; -import com.powsybl.iidm.network.LimitType; -import com.powsybl.iidm.network.PhaseTapChanger; -import com.powsybl.iidm.network.RatioTapChanger; -import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.*; import com.powsybl.openloadflow.network.*; import com.powsybl.openloadflow.util.PerUnit; import com.powsybl.security.results.BranchResult; @@ -22,14 +19,22 @@ */ public class LfLegBranch extends AbstractImpedantLfBranch { - private final ThreeWindingsTransformer twt; + private final Ref twtRef; - private final ThreeWindingsTransformer.Leg leg; + private final Ref legRef; protected LfLegBranch(LfNetwork network, LfBus bus1, LfBus bus0, PiModel piModel, ThreeWindingsTransformer twt, ThreeWindingsTransformer.Leg leg) { super(network, bus1, bus0, piModel); - this.twt = twt; - this.leg = leg; + this.twtRef = new Ref<>(twt); + this.legRef = new Ref<>(leg); + } + + private ThreeWindingsTransformer getTwt() { + return twtRef.get(); + } + + private ThreeWindingsTransformer.Leg getLeg() { + return legRef.get(); } public static LfLegBranch create(LfNetwork network, LfBus bus1, LfBus bus0, ThreeWindingsTransformer twt, ThreeWindingsTransformer.Leg leg, @@ -86,6 +91,8 @@ public static LfLegBranch create(LfNetwork network, LfBus bus1, LfBus bus0, Thre } private int getLegNum() { + var twt = getTwt(); + var leg = getLeg(); if (leg == twt.getLeg1()) { return 1; } else if (leg == twt.getLeg2()) { @@ -101,11 +108,13 @@ public static String getId(String twtId, int legNum) { @Override public String getId() { - return getId(twt.getId(), getLegNum()); + return getId(getTwt().getId(), getLegNum()); } @Override public BranchType getBranchType() { + var twt = getTwt(); + var leg = getLeg(); if (leg == twt.getLeg1()) { return BranchType.TRANSFO_3_LEG_1; } else if (leg == twt.getLeg2()) { @@ -117,12 +126,12 @@ public BranchType getBranchType() { @Override public List getOriginalIds() { - return List.of(twt.getId()); + return List.of(getTwt().getId()); } @Override public boolean hasPhaseControlCapability() { - return leg.getPhaseTapChanger() != null; + return getLeg().getPhaseTapChanger() != null; } @Override @@ -132,6 +141,7 @@ public BranchResult createBranchResult(double preContingencyBranchP1, double pre @Override public List getLimits1(final LimitType type) { + var leg = getLeg(); switch (type) { case ACTIVE_POWER: return getLimits1(type, leg.getActivePowerLimits().orElse(null)); @@ -147,6 +157,9 @@ public List getLimits1(final LimitType type) { @Override public void updateState(boolean phaseShifterRegulationOn, boolean isTransformerVoltageControlOn, boolean dc) { + var twt = getTwt(); + var leg = getLeg(); + updateFlows(p1.eval(), q1.eval(), Double.NaN, Double.NaN); if (phaseShifterRegulationOn && isPhaseController()) { @@ -172,7 +185,7 @@ public void updateState(boolean phaseShifterRegulationOn, boolean isTransformerV @Override public void updateFlows(double p1, double q1, double p2, double q2) { // Star bus is always on side 2. - leg.getTerminal().setP(p1 * PerUnit.SB) + getLeg().getTerminal().setP(p1 * PerUnit.SB) .setQ(q1 * PerUnit.SB); } } diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfNetworkList.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfNetworkList.java index c2f17b4907..c1c3bb4fbe 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfNetworkList.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfNetworkList.java @@ -41,7 +41,7 @@ public void clean() { // list of networks sorted by descending size private final List list; - private final VariantCleaner variantCleaner; + private VariantCleaner variantCleaner; public LfNetworkList(List list, VariantCleaner variantCleaner) { this.list = Objects.requireNonNull(list); @@ -60,6 +60,12 @@ public Optional getLargest() { return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0)); } + public VariantCleaner release() { + VariantCleaner variantCleanerToReturn = variantCleaner; + variantCleaner = null; + return variantCleanerToReturn; + } + @Override public void close() { if (variantCleaner != null) { diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfShuntImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfShuntImpl.java index fd69d91833..10b14d5f5e 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfShuntImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfShuntImpl.java @@ -6,7 +6,10 @@ */ package com.powsybl.openloadflow.network.impl; -import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.ShuntCompensator; +import com.powsybl.iidm.network.ShuntCompensatorLinearModel; +import com.powsybl.iidm.network.ShuntCompensatorModel; +import com.powsybl.iidm.network.ShuntCompensatorNonLinearModel; import com.powsybl.openloadflow.network.*; import com.powsybl.openloadflow.util.PerUnit; import org.slf4j.Logger; @@ -23,7 +26,7 @@ public class LfShuntImpl extends AbstractElement implements LfShunt { private static final Logger LOGGER = LoggerFactory.getLogger(LfShuntImpl.class); - private final List shuntCompensators; + private final List> shuntCompensatorsRefs; private final LfBus bus; @@ -97,7 +100,7 @@ public LfShuntImpl(List shuntCompensators, LfNetwork network, // if withVoltageControl equals to false, all shunt compensators that are listed will be treated as fixed shunt // compensators. super(network); - this.shuntCompensators = Objects.requireNonNull(shuntCompensators); + shuntCompensatorsRefs = Objects.requireNonNull(shuntCompensators).stream().map(Ref::new).collect(Collectors.toList()); if (shuntCompensators.isEmpty()) { throw new IllegalArgumentException("Empty shunt compensator list"); } @@ -152,7 +155,7 @@ public String getId() { @Override public List getOriginalIds() { - return shuntCompensators.stream().map(ShuntCompensator::getId).collect(Collectors.toList()); + return shuntCompensatorsRefs.stream().map(scRef -> scRef.get().getId()).collect(Collectors.toList()); } @Override @@ -243,19 +246,21 @@ public double dispatchB() { @Override public void updateState(boolean dc) { if (dc) { - for (ShuntCompensator sc : shuntCompensators) { + for (var scRef : shuntCompensatorsRefs) { + var sc = scRef.get(); sc.getTerminal().setP(0); } } else { double vSquare = bus.getV() * bus.getV() * bus.getNominalV() * bus.getNominalV(); if (!voltageControlCapability) { - for (ShuntCompensator sc : shuntCompensators) { + for (var scRef : shuntCompensatorsRefs) { + var sc = scRef.get(); sc.getTerminal().setP(sc.getG() * vSquare); sc.getTerminal().setQ(-sc.getB() * vSquare); } } else { - for (int i = 0; i < shuntCompensators.size(); i++) { - ShuntCompensator sc = shuntCompensators.get(i); + for (int i = 0; i < shuntCompensatorsRefs.size(); i++) { + ShuntCompensator sc = shuntCompensatorsRefs.get(i).get(); sc.getTerminal().setP(controllers.get(i).getG() * vSquare / zb); sc.getTerminal().setQ(-controllers.get(i).getB() * vSquare / zb); sc.setSectionCount(controllers.get(i).getPosition()); diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfStarBus.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfStarBus.java index 3a12844d9d..0f9b5ad529 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfStarBus.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfStarBus.java @@ -16,33 +16,37 @@ */ public class LfStarBus extends AbstractLfBus { - private final ThreeWindingsTransformer t3wt; + private final Ref t3wtRef; private final double nominalV; public LfStarBus(LfNetwork network, ThreeWindingsTransformer t3wt) { super(network, Networks.getPropertyV(t3wt), Networks.getPropertyAngle(t3wt), false); - this.t3wt = t3wt; + this.t3wtRef = new Ref<>(t3wt); nominalV = t3wt.getRatedU0(); } + private ThreeWindingsTransformer getT3wt() { + return t3wtRef.get(); + } + public static String getId(String id) { return id + "_BUS0"; } @Override public String getId() { - return getId(t3wt.getId()); + return getId(getT3wt().getId()); } @Override public List getOriginalIds() { - return List.of(t3wt.getId()); + return List.of(getT3wt().getId()); } @Override public String getVoltageLevelId() { - return t3wt.getLeg1().getTerminal().getVoltageLevel().getId(); + return getT3wt().getLeg1().getTerminal().getVoltageLevel().getId(); } @Override @@ -57,6 +61,7 @@ public double getNominalV() { @Override public void updateState(boolean reactiveLimits, boolean writeSlackBus, boolean distributedOnConformLoad, boolean loadPowerFactorConstant) { + var t3wt = getT3wt(); Networks.setPropertyV(t3wt, v); Networks.setPropertyAngle(t3wt, angle); diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfStaticVarCompensatorImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfStaticVarCompensatorImpl.java index 24abf4a6af..e26c8f68bf 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfStaticVarCompensatorImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfStaticVarCompensatorImpl.java @@ -20,7 +20,7 @@ */ public final class LfStaticVarCompensatorImpl extends AbstractLfGenerator { - private final StaticVarCompensator svc; + private final Ref svcRef; private final ReactiveLimits reactiveLimits; @@ -32,20 +32,20 @@ private LfStaticVarCompensatorImpl(StaticVarCompensator svc, LfNetwork network, boolean breakers, boolean reactiveLimits, LfNetworkLoadingReport report, double minPlausibleTargetVoltage, double maxPlausibleTargetVoltage, OpenLoadFlowParameters.ReactiveRangeCheckMode reactiveRangeCheckMode) { super(network, 0); - this.svc = svc; + this.svcRef = new Ref<>(svc); this.nominalV = svc.getTerminal().getVoltageLevel().getNominalV(); this.reactiveLimits = new MinMaxReactiveLimits() { @Override public double getMinQ() { double v = bus.getV() * nominalV; - return svc.getBmin() * v * v; + return svcRef.get().getBmin() * v * v; } @Override public double getMaxQ() { double v = bus.getV() * nominalV; - return svc.getBmax() * v * v; + return svcRef.get().getBmax() * v * v; } @Override @@ -81,14 +81,18 @@ public static LfStaticVarCompensatorImpl create(StaticVarCompensator svc, LfNetw report, minPlausibleTargetVoltage, maxPlausibleTargetVoltage, reactiveRangeCheckMode); } + private StaticVarCompensator getSvc() { + return svcRef.get(); + } + @Override public String getId() { - return svc.getId(); + return getSvc().getId(); } @Override public double getTargetQ() { - return -svc.getReactivePowerSetpoint() / PerUnit.SB; + return -getSvc().getReactivePowerSetpoint() / PerUnit.SB; } @Override @@ -108,6 +112,7 @@ protected Optional getReactiveLimits() { @Override public void updateState() { + var svc = getSvc(); svc.getTerminal() .setP(0) .setQ(Double.isNaN(calculatedQ) ? svc.getReactivePowerSetpoint() : -calculatedQ); diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfSwitch.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfSwitch.java index 7468bd5ee8..d33b793eba 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfSwitch.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfSwitch.java @@ -9,13 +9,14 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.LimitType; import com.powsybl.iidm.network.Switch; -import com.powsybl.openloadflow.network.*; +import com.powsybl.openloadflow.network.LfBus; +import com.powsybl.openloadflow.network.LfNetwork; +import com.powsybl.openloadflow.network.SimplePiModel; import com.powsybl.openloadflow.util.Evaluable; import com.powsybl.security.results.BranchResult; import java.util.Collections; import java.util.List; -import java.util.Objects; import static com.powsybl.openloadflow.util.EvaluableConstants.NAN; @@ -24,16 +25,20 @@ */ public class LfSwitch extends AbstractLfBranch { - private final Switch aSwitch; + private final Ref switchRef; public LfSwitch(LfNetwork network, LfBus bus1, LfBus bus2, Switch aSwitch) { super(network, bus1, bus2, new SimplePiModel()); - this.aSwitch = Objects.requireNonNull(aSwitch); + this.switchRef = new Ref<>(aSwitch); + } + + private Switch getSwitch() { + return switchRef.get(); } @Override public String getId() { - return aSwitch.getId(); + return getSwitch().getId(); } @Override @@ -108,7 +113,7 @@ public Evaluable getI2() { @Override public BranchResult createBranchResult(double preContingencyBranchP1, double preContingencyBranchOfContingencyP1, boolean createExtension) { - throw new PowsyblException("Unsupported type of branch for branch result: " + aSwitch.getId()); + throw new PowsyblException("Unsupported type of branch for branch result: " + getSwitch().getId()); } public List getLimits1(final LimitType type) { diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java index ee81fc4def..3134065c61 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java @@ -20,14 +20,14 @@ */ public class LfVscConverterStationImpl extends AbstractLfGenerator implements LfVscConverterStation { - private final VscConverterStation station; + private final Ref stationRef; private final double lossFactor; public LfVscConverterStationImpl(VscConverterStation station, LfNetwork network, boolean breakers, boolean reactiveLimits, LfNetworkLoadingReport report, double minPlausibleTargetVoltage, double maxPlausibleTargetVoltage, OpenLoadFlowParameters.ReactiveRangeCheckMode reactiveRangeCheckMode) { super(network, HvdcConverterStations.getConverterStationTargetP(station)); - this.station = station; + this.stationRef = new Ref<>(station); this.lossFactor = station.getLossFactor(); // local control only @@ -43,6 +43,10 @@ public static LfVscConverterStationImpl create(VscConverterStation station, LfNe return new LfVscConverterStationImpl(station, network, breakers, reactiveLimits, report, minPlausibleTargetVoltage, maxPlausibleTargetVoltage, reactiveRangeCheckMode); } + private VscConverterStation getStation() { + return stationRef.get(); + } + @Override public double getLossFactor() { return lossFactor; @@ -50,31 +54,32 @@ public double getLossFactor() { @Override public String getId() { - return station.getId(); + return getStation().getId(); } @Override public double getTargetQ() { - return station.getReactivePowerSetpoint() / PerUnit.SB; + return getStation().getReactivePowerSetpoint() / PerUnit.SB; } @Override public double getMinP() { - return -station.getHvdcLine().getMaxP() / PerUnit.SB; + return -getStation().getHvdcLine().getMaxP() / PerUnit.SB; } @Override public double getMaxP() { - return station.getHvdcLine().getMaxP() / PerUnit.SB; + return getStation().getHvdcLine().getMaxP() / PerUnit.SB; } @Override protected Optional getReactiveLimits() { - return Optional.of(station.getReactiveLimits()); + return Optional.of(getStation().getReactiveLimits()); } @Override public void updateState() { + var station = getStation(); station.getTerminal() .setP(-targetP) .setQ(Double.isNaN(calculatedQ) ? -station.getReactivePowerSetpoint() : -calculatedQ); diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/Ref.java b/src/main/java/com/powsybl/openloadflow/network/impl/Ref.java new file mode 100644 index 0000000000..53092c8944 --- /dev/null +++ b/src/main/java/com/powsybl/openloadflow/network/impl/Ref.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.openloadflow.network.impl; + +import java.lang.ref.WeakReference; +import java.util.Objects; + +/** + * @author Geoffroy Jamgotchian + */ +public class Ref { + + private final WeakReference value; + + public Ref(T identifiable) { + this.value = new WeakReference<>(Objects.requireNonNull(identifiable)); + } + + public T get() { + return Objects.requireNonNull(value.get(), "Reference has been garbage collected"); + } +} diff --git a/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java b/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java index d3f3daf019..2dd3b25330 100644 --- a/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java +++ b/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java @@ -243,4 +243,30 @@ void testUpdateParameters() { assertEquals(10, parameters.getMaxIteration()); assertFalse(parameters.hasReactivePowerRemoteControl()); } + + @Test + void testCompareParameters() { + assertTrue(OpenLoadFlowParameters.equals(new LoadFlowParameters(), new LoadFlowParameters())); + assertFalse(OpenLoadFlowParameters.equals(new LoadFlowParameters(), new LoadFlowParameters().setDc(true))); + var p1 = new LoadFlowParameters(); + var p2 = new LoadFlowParameters(); + var pe1 = OpenLoadFlowParameters.create(p1); + OpenLoadFlowParameters.create(p2); + assertTrue(OpenLoadFlowParameters.equals(p1, p2)); + assertFalse(OpenLoadFlowParameters.equals(p1, new LoadFlowParameters())); + assertFalse(OpenLoadFlowParameters.equals(new LoadFlowParameters(), p2)); + pe1.setDcPowerFactor(0.3); + assertFalse(OpenLoadFlowParameters.equals(p1, p2)); + } + + @Test + void testCloneParameters() { + var p = new LoadFlowParameters(); + assertTrue(OpenLoadFlowParameters.equals(p, OpenLoadFlowParameters.clone(p))); + var pe = OpenLoadFlowParameters.create(p); + assertTrue(OpenLoadFlowParameters.equals(p, OpenLoadFlowParameters.clone(p))); + pe.setMaxIteration(20); + assertTrue(OpenLoadFlowParameters.equals(p, OpenLoadFlowParameters.clone(p))); + assertFalse(OpenLoadFlowParameters.equals(new LoadFlowParameters(), OpenLoadFlowParameters.clone(p))); + } } diff --git a/src/test/java/com/powsybl/openloadflow/OpenLoadFlowProviderTest.java b/src/test/java/com/powsybl/openloadflow/OpenLoadFlowProviderTest.java index c026829cce..07fc8cd8ec 100644 --- a/src/test/java/com/powsybl/openloadflow/OpenLoadFlowProviderTest.java +++ b/src/test/java/com/powsybl/openloadflow/OpenLoadFlowProviderTest.java @@ -85,7 +85,7 @@ void testGetExtendedVoltageInitializer() { @Test void specificParametersTest() { OpenLoadFlowProvider provider = new OpenLoadFlowProvider(); - assertEquals(21, provider.getSpecificParametersNames().size()); + assertEquals(22, provider.getSpecificParametersNames().size()); LoadFlowParameters parameters = new LoadFlowParameters(); provider.loadSpecificParameters(Collections.emptyMap()) diff --git a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowWithCachingTest.java b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowWithCachingTest.java new file mode 100644 index 0000000000..3b941f0e4d --- /dev/null +++ b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowWithCachingTest.java @@ -0,0 +1,186 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.openloadflow.ac; + +import com.powsybl.iidm.network.VariantManagerConstants; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import com.powsybl.loadflow.LoadFlow; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.loadflow.LoadFlowResult; +import com.powsybl.math.matrix.DenseMatrixFactory; +import com.powsybl.openloadflow.NetworkCache; +import com.powsybl.openloadflow.OpenLoadFlowParameters; +import com.powsybl.openloadflow.OpenLoadFlowProvider; +import com.powsybl.openloadflow.network.EurostagFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static com.powsybl.openloadflow.util.LoadFlowAssert.assertVoltageEquals; +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Geoffroy Jamgotchian + */ +class AcLoadFlowWithCachingTest { + + private LoadFlow.Runner loadFlowRunner; + + private LoadFlowParameters parameters; + + @BeforeEach + void setUp() { + loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(new DenseMatrixFactory())); + parameters = new LoadFlowParameters(); + OpenLoadFlowParameters.create(parameters) + .setNetworkCacheEnabled(true); + NetworkCache.INSTANCE.clear(); + } + + @Test + void testTargetV() { + var network = EurostagFactory.fix(EurostagTutorialExample1Factory.create()); + var gen = network.getGenerator("GEN"); + var ngen = network.getBusBreakerView().getBus("NGEN"); + var nload = network.getBusBreakerView().getBus("NLOAD"); + + assertEquals(0, NetworkCache.INSTANCE.getEntryCount()); + var result = loadFlowRunner.run(network, parameters); + assertEquals(1, NetworkCache.INSTANCE.getEntryCount()); + assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus()); + assertEquals(4, result.getComponentResults().get(0).getIterationCount()); + assertVoltageEquals(24.5, ngen); + assertVoltageEquals(147.578, nload); + + gen.setTargetV(24.1); + + result = loadFlowRunner.run(network, parameters); + assertEquals(1, NetworkCache.INSTANCE.getEntryCount()); + assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus()); + assertEquals(2, result.getComponentResults().get(0).getIterationCount()); + assertVoltageEquals(24.1, ngen); + assertVoltageEquals(144.402, nload); + + result = loadFlowRunner.run(network, parameters); + assertEquals(1, NetworkCache.INSTANCE.getEntryCount()); + // FIXME NO_CALCULATION should be added to API + assertEquals(LoadFlowResult.ComponentResult.Status.FAILED, result.getComponentResults().get(0).getStatus()); + assertEquals(0, result.getComponentResults().get(0).getIterationCount()); + } + + @Test + void testParameterChange() { + var network = EurostagFactory.fix(EurostagTutorialExample1Factory.create()); + + assertEquals(0, NetworkCache.INSTANCE.getEntryCount()); + loadFlowRunner.run(network, parameters); + assertEquals(1, NetworkCache.INSTANCE.getEntryCount()); + NetworkCache.Entry entry = NetworkCache.INSTANCE.findEntry(network).orElseThrow(); + loadFlowRunner.run(network, parameters); + assertEquals(1, NetworkCache.INSTANCE.getEntryCount()); + NetworkCache.Entry entry2 = NetworkCache.INSTANCE.findEntry(network).orElseThrow(); + assertSame(entry, entry2); // reuse same cache + + // run with different parameters + parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD); + loadFlowRunner.run(network, parameters); + assertEquals(1, NetworkCache.INSTANCE.getEntryCount()); + NetworkCache.Entry entry3 = NetworkCache.INSTANCE.findEntry(network).orElseThrow(); + assertNotSame(entry, entry3); // cache has been evicted and recreated + } + + @Test + @Disabled("Disabled by default because not reliable, depends on JVM, garbage collector, and machine performance") + void testCacheEviction() { + int runCount = 10; + for (int i = 0; i < runCount; i++) { + var network = EurostagFactory.fix(EurostagTutorialExample1Factory.create()); + loadFlowRunner.run(network, parameters); + System.gc(); + } + assertTrue(NetworkCache.INSTANCE.getEntryCount() < runCount); + } + + @Test + void testUnsupportedAttributeChange() { + var network = EurostagFactory.fix(EurostagTutorialExample1Factory.create()); + var gen = network.getGenerator("GEN"); + + loadFlowRunner.run(network, parameters); + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + gen.setTargetQ(10); + assertNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + } + + @Test + void testPropertiesChange() { + var network = EurostagFactory.fix(EurostagTutorialExample1Factory.create()); + var gen = network.getGenerator("GEN"); + + loadFlowRunner.run(network, parameters); + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + gen.setProperty("foo", "bar"); + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + gen.setProperty("foo", "baz"); + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + gen.removeProperty("foo"); + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + } + + @Test + void testVariantChange() { + var network = EurostagFactory.fix(EurostagTutorialExample1Factory.create()); + + loadFlowRunner.run(network, parameters); + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + + network.getVariantManager().cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, "v"); + assertNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + + loadFlowRunner.run(network, parameters); + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + + network.getVariantManager().cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, "v", true); + assertNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + + loadFlowRunner.run(network, parameters); + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + + network.getVariantManager().removeVariant("v"); + assertNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + } + + @Test + void testLoadAddition() { + var network = EurostagFactory.fix(EurostagTutorialExample1Factory.create()); + + loadFlowRunner.run(network, parameters); + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + + network.getVoltageLevel("VLLOAD").newLoad() + .setId("NEWLOAD") + .setConnectableBus("NLOAD") + .setBus("NLOAD") + .setP0(10) + .setQ0(10) + .add(); + + assertNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + } + + @Test + void testLoadRemoval() { + var network = EurostagFactory.fix(EurostagTutorialExample1Factory.create()); + + loadFlowRunner.run(network, parameters); + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + + network.getLoad("LOAD").remove(); + + assertNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + } +} diff --git a/src/test/java/com/powsybl/openloadflow/graph/BridgesTest.java b/src/test/java/com/powsybl/openloadflow/graph/BridgesTest.java index e0f0cdb030..a5a98185fe 100644 --- a/src/test/java/com/powsybl/openloadflow/graph/BridgesTest.java +++ b/src/test/java/com/powsybl/openloadflow/graph/BridgesTest.java @@ -38,13 +38,14 @@ class BridgesTest { private static final Logger LOGGER = LoggerFactory.getLogger(BridgesTest.class); + private Network network; private LfNetwork lfNetwork; private Set bridgesSetReference; @BeforeEach void setUp() { long start = System.currentTimeMillis(); - Network network = EurostagTutorialExample1Factory.create(); + network = EurostagTutorialExample1Factory.create(); List lfn = Networks.load(network, new FirstSlackBusSelector()); this.lfNetwork = lfn.get(0); LOGGER.info("Reading network of {} buses in {} ms", lfNetwork.getBuses().size(), System.currentTimeMillis() - start); diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisGraphTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisGraphTest.java index 94197b0928..5c16f78a32 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisGraphTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisGraphTest.java @@ -12,10 +12,6 @@ import com.powsybl.contingency.Contingency; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.Switch; -import com.powsybl.loadflow.LoadFlowParameters; -import com.powsybl.math.matrix.DenseMatrixFactory; -import com.powsybl.openloadflow.OpenLoadFlowParameters; -import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters; import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory; import com.powsybl.openloadflow.graph.GraphConnectivityFactory; import com.powsybl.openloadflow.graph.MinimumSpanningTreeGraphConnectivityFactory; @@ -24,7 +20,6 @@ import com.powsybl.openloadflow.network.impl.LfNetworkList; import com.powsybl.openloadflow.network.impl.Networks; import com.powsybl.openloadflow.network.impl.PropagatedContingency; -import com.powsybl.security.SecurityAnalysisParameters; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -45,62 +40,63 @@ class OpenSecurityAnalysisGraphTest { private Network network; private ContingenciesProvider contingenciesProvider; - private SecurityAnalysisParameters securityAnalysisParameters; @BeforeEach void setUp() { - network = NodeBreakerNetworkFactory.create(); // Testing all contingencies at once contingenciesProvider = network -> network.getBranchStream() .map(b -> new Contingency(b.getId(), new BranchContingency(b.getId()))) .collect(Collectors.toList()); + } - LoadFlowParameters lfParameters = new LoadFlowParameters(); - OpenLoadFlowParameters.create(lfParameters) - .setSlackBusSelectionMode(SlackBusSelectionMode.FIRST); - securityAnalysisParameters = new SecurityAnalysisParameters().setLoadFlowParameters(lfParameters); + private TestData computeReferenceLfContingencies() { + var testData = computeLfContingencies(new NaiveGraphConnectivityFactory<>(LfBus::getNum)); + LOGGER.info("Reference established (naive connectivity calculation) on test network containing {} branches", network.getBranchCount()); + return testData; } @Test void testEvenShiloach() { LOGGER.info("Test Even-Shiloach on test network containing {} branches", network.getBranchCount()); - List> lfContingencies = getLoadFlowContingencies(new EvenShiloachGraphDecrementalConnectivityFactory<>()); - printResult(lfContingencies); - checkResult(lfContingencies, computeReference()); + try (var testDataRef = computeReferenceLfContingencies(); + var testData = computeLfContingencies(new EvenShiloachGraphDecrementalConnectivityFactory<>())) { + printResult(testData.getListLfContingencies()); + checkResult(testData.getListLfContingencies(), testDataRef.getListLfContingencies()); + } } @Test void testMst() { LOGGER.info("Test Minimum Spanning Tree on test network containing {} branches", network.getBranchCount()); - List> lfContingencies = getLoadFlowContingencies(new MinimumSpanningTreeGraphConnectivityFactory<>()); - printResult(lfContingencies); - checkResult(lfContingencies, computeReference()); - } - - private List> computeReference() { - List> result = getLoadFlowContingencies(new NaiveGraphConnectivityFactory<>(LfBus::getNum)); - LOGGER.info("Reference established (naive connectivity calculation) on test network containing {} branches", network.getBranchCount()); - return result; + try (var testDataRef = computeReferenceLfContingencies(); + var testData = computeLfContingencies(new MinimumSpanningTreeGraphConnectivityFactory<>())) { + printResult(testData.getListLfContingencies()); + checkResult(testData.getListLfContingencies(), testDataRef.getListLfContingencies()); + } } @Test void testNullVertices() { network.getSwitch("B3").setOpen(true); - contingenciesProvider = n -> Collections.singletonList( - new Contingency("L1", new BranchContingency("L1"))); - List> reference = computeReference(); - checkResult(getLoadFlowContingencies(new MinimumSpanningTreeGraphConnectivityFactory<>()), reference); - checkResult(getLoadFlowContingencies(new EvenShiloachGraphDecrementalConnectivityFactory<>()), reference); - - contingenciesProvider = n -> Collections.singletonList( - new Contingency("L2", new BranchContingency("L2"))); + contingenciesProvider = n -> Collections.singletonList(new Contingency("L1", new BranchContingency("L1"))); + try (var testDataRef = computeReferenceLfContingencies(); + var testData1 = computeLfContingencies(new MinimumSpanningTreeGraphConnectivityFactory<>()); + var testData2 = computeLfContingencies(new EvenShiloachGraphDecrementalConnectivityFactory<>())) { + checkResult(testData1.getListLfContingencies(), testDataRef.getListLfContingencies()); + checkResult(testData2.getListLfContingencies(), testDataRef.getListLfContingencies()); + } + + contingenciesProvider = n -> Collections.singletonList(new Contingency("L2", new BranchContingency("L2"))); network.getSwitch("B3").setOpen(false); network.getSwitch("B1").setOpen(true); - reference = computeReference(); - checkResult(getLoadFlowContingencies(new MinimumSpanningTreeGraphConnectivityFactory<>()), reference); - checkResult(getLoadFlowContingencies(new EvenShiloachGraphDecrementalConnectivityFactory<>()), reference); + try (var testDataRef = computeReferenceLfContingencies(); + var testData1 = computeLfContingencies(new MinimumSpanningTreeGraphConnectivityFactory<>()); + var testData2 = computeLfContingencies(new EvenShiloachGraphDecrementalConnectivityFactory<>())) { + checkResult(testData1.getListLfContingencies(), testDataRef.getListLfContingencies()); + checkResult(testData2.getListLfContingencies(), testDataRef.getListLfContingencies()); + } } private static void checkResult(List> result, List> reference) { @@ -136,43 +132,53 @@ private void printResult(List> result) { } } - List> getLoadFlowContingencies(GraphConnectivityFactory connectivityFactory) { + private static class TestData implements AutoCloseable { - var matrixFactory = new DenseMatrixFactory(); - AcSecurityAnalysis securityAnalysis = new AcSecurityAnalysis(network, matrixFactory, connectivityFactory, Collections.emptyList(), Reporter.NO_OP); + private final LfNetworkList lfNetworks; + private final List> listLfContingencies; - LoadFlowParameters lfParameters = securityAnalysisParameters.getLoadFlowParameters(); - OpenLoadFlowParameters lfParametersExt = OpenLoadFlowParameters.get(securityAnalysisParameters.getLoadFlowParameters()); + public TestData(LfNetworkList lfNetworks, List> listLfContingencies) { + this.lfNetworks = lfNetworks; + this.listLfContingencies = listLfContingencies; + } + public LfNetworkList getLfNetworks() { + return lfNetworks; + } + + public List> getListLfContingencies() { + return listLfContingencies; + } + + @Override + public void close() { + lfNetworks.close(); + } + } + + private TestData computeLfContingencies(GraphConnectivityFactory connectivityFactory) { // load contingencies List contingencies = contingenciesProvider.getContingencies(network); // try to find all switches impacted by at least one contingency - long start = System.currentTimeMillis(); Set allSwitchesToOpen = new HashSet<>(); - List propagatedContingencies = PropagatedContingency.createList(network, contingencies, allSwitchesToOpen, - lfParameters.isShuntCompensatorVoltageControlOn(), lfParameters.getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD, - lfParameters.isHvdcAcEmulation() && !lfParameters.isDc(), true); - LOGGER.info("Contingencies contexts calculated from contingencies in {} ms", System.currentTimeMillis() - start); + List propagatedContingencies = PropagatedContingency.createList(network, contingencies, allSwitchesToOpen, false, false, false, true); - AcLoadFlowParameters acParameters = OpenLoadFlowParameters.createAcParameters(network, - lfParameters, lfParametersExt, matrixFactory, connectivityFactory, true, false); + LfNetworkParameters networkParameters = new LfNetworkParameters() + .setConnectivityFactory(connectivityFactory) + .setBreakers(true); // create networks including all necessary switches - try (LfNetworkList lfNetworks = Networks.load(network, acParameters.getNetworkParameters(), allSwitchesToOpen, Collections.emptySet(), Reporter.NO_OP)) { - - // run simulation on each network - start = System.currentTimeMillis(); - List> listLfContingencies = new ArrayList<>(); - for (LfNetwork lfNetwork : lfNetworks.getList()) { - listLfContingencies.add(propagatedContingencies.stream() - .flatMap(propagatedContingency -> propagatedContingency.toLfContingency(lfNetwork).stream()) - .collect(Collectors.toList())); - } - LOGGER.info("LoadFlow contingencies calculated from contingency contexts in {} ms", System.currentTimeMillis() - start); - - return listLfContingencies; + LfNetworkList lfNetworks = Networks.load(network, networkParameters, allSwitchesToOpen, Collections.emptySet(), Reporter.NO_OP); + + // run simulation on each network + List> listLfContingencies = new ArrayList<>(); + for (LfNetwork lfNetwork : lfNetworks.getList()) { + listLfContingencies.add(propagatedContingencies.stream() + .flatMap(propagatedContingency -> propagatedContingency.toLfContingency(lfNetwork).stream()) + .collect(Collectors.toList())); } - } + return new TestData(lfNetworks, listLfContingencies); + } } diff --git a/src/test/resources/debug-parameters.json b/src/test/resources/debug-parameters.json index 47e57a0bd8..e9f1d829cc 100644 --- a/src/test/resources/debug-parameters.json +++ b/src/test/resources/debug-parameters.json @@ -37,7 +37,8 @@ "minRealisticVoltage" : 0.5, "maxRealisticVoltage" : 1.5, "lowImpedanceThreshold" : 1.0E-8, - "reactiveRangeCheckMode" : "MAX" + "reactiveRangeCheckMode" : "MAX", + "networkCacheEnabled" : false } } },