com.powsybl.openloadflow.OpenLoadFlowProvider Maven / Gradle / Ivy
/**
* Copyright (c) 2018, 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/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.openloadflow;
import com.google.auto.service.AutoService;
import com.google.common.base.Stopwatch;
import com.powsybl.commons.config.ModuleConfig;
import com.powsybl.commons.config.PlatformConfig;
import com.powsybl.commons.extensions.Extension;
import com.powsybl.commons.extensions.ExtensionJsonSerializer;
import com.powsybl.commons.parameters.Parameter;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.computation.ComputationManager;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.extensions.ReferenceTerminals;
import com.powsybl.iidm.network.extensions.SlackTerminal;
import com.powsybl.loadflow.LoadFlowParameters;
import com.powsybl.loadflow.LoadFlowProvider;
import com.powsybl.loadflow.LoadFlowResult;
import com.powsybl.loadflow.LoadFlowResultImpl;
import com.powsybl.math.matrix.MatrixFactory;
import com.powsybl.math.matrix.SparseMatrixFactory;
import com.powsybl.openloadflow.ac.AcLoadFlowParameters;
import com.powsybl.openloadflow.ac.AcLoadFlowResult;
import com.powsybl.openloadflow.ac.AcloadFlowEngine;
import com.powsybl.openloadflow.dc.DcLoadFlowEngine;
import com.powsybl.openloadflow.dc.DcLoadFlowResult;
import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory;
import com.powsybl.openloadflow.graph.GraphConnectivityFactory;
import com.powsybl.openloadflow.graph.NaiveGraphConnectivityFactory;
import com.powsybl.openloadflow.lf.AbstractLoadFlowResult;
import com.powsybl.openloadflow.lf.outerloop.OuterLoop;
import com.powsybl.openloadflow.network.*;
import com.powsybl.openloadflow.network.impl.LfNetworkList;
import com.powsybl.openloadflow.network.impl.LfNetworkLoaderImpl;
import com.powsybl.openloadflow.network.impl.Networks;
import com.powsybl.openloadflow.network.util.ZeroImpedanceFlows;
import com.powsybl.openloadflow.util.*;
import com.powsybl.tools.PowsyblCoreVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.powsybl.openloadflow.OpenLoadFlowParameters.MODULE_SPECIFIC_PARAMETERS;
/**
* @author Sylvain Leclerc {@literal }
*/
@AutoService(LoadFlowProvider.class)
public class OpenLoadFlowProvider implements LoadFlowProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(OpenLoadFlowProvider.class);
private final MatrixFactory matrixFactory;
private final GraphConnectivityFactory connectivityFactory;
private boolean forcePhaseControlOffAndAddAngle1Var = false; // just for unit testing
public OpenLoadFlowProvider() {
this(new SparseMatrixFactory());
}
public OpenLoadFlowProvider(MatrixFactory matrixFactory) {
this(matrixFactory, new EvenShiloachGraphDecrementalConnectivityFactory<>());
}
public OpenLoadFlowProvider(MatrixFactory matrixFactory, GraphConnectivityFactory connectivityFactory) {
this.matrixFactory = Objects.requireNonNull(matrixFactory);
this.connectivityFactory = Objects.requireNonNull(connectivityFactory);
}
public void setForcePhaseControlOffAndAddAngle1Var(boolean forcePhaseControlOffAndAddAngle1Var) {
this.forcePhaseControlOffAndAddAngle1Var = forcePhaseControlOffAndAddAngle1Var;
}
@Override
public String getName() {
return ProviderConstants.NAME;
}
@Override
public String getVersion() {
return new PowsyblCoreVersion().getMavenProjectVersion();
}
private GraphConnectivityFactory getConnectivityFactory(OpenLoadFlowParameters parametersExt) {
return parametersExt.isNetworkCacheEnabled() && !parametersExt.getActionableSwitchesIds().isEmpty()
|| parametersExt.isSimulateAutomationSystems()
? new NaiveGraphConnectivityFactory<>(LfBus::getNum)
: connectivityFactory;
}
private void updateAcState(Network network, LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt,
AcLoadFlowResult result, AcLoadFlowParameters acParameters, boolean atLeastOneComponentHasToBeUpdated) {
if (parametersExt.isNetworkCacheEnabled()) {
NetworkCache.INSTANCE.findEntry(network).orElseThrow().setPause(true);
}
try {
// update network state
if (atLeastOneComponentHasToBeUpdated && result.isSuccess()
|| parametersExt.isAlwaysUpdateNetwork()) {
var updateParameters = new LfNetworkStateUpdateParameters(parameters.isUseReactiveLimits(),
parameters.isWriteSlackBus(),
parameters.isPhaseShifterRegulationOn(),
parameters.isTransformerVoltageControlOn(),
parametersExt.isTransformerReactivePowerControl(),
(parameters.isDistributedSlack() || parametersExt.isAreaInterchangeControl()) && (parameters.getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD || parameters.getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD) && parametersExt.isLoadPowerFactorConstant(),
parameters.isDc(),
acParameters.getNetworkParameters().isBreakers(),
parametersExt.getReactivePowerDispatchMode(),
parametersExt.isWriteReferenceTerminals(),
parametersExt.getReferenceBusSelectionMode(),
parametersExt.isSimulateAutomationSystems());
result.getNetwork().updateState(updateParameters);
// zero or low impedance branch flows computation
computeZeroImpedanceFlows(result.getNetwork(), LoadFlowModel.AC);
}
} finally {
if (parametersExt.isNetworkCacheEnabled()) {
NetworkCache.INSTANCE.findEntry(network).orElseThrow().setPause(false);
}
}
}
private LoadFlowResult runAc(Network network, LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt, ReportNode reportNode) {
GraphConnectivityFactory selectedConnectivityFactory = getConnectivityFactory(parametersExt);
AcLoadFlowParameters acParameters = OpenLoadFlowParameters.createAcParameters(network, parameters, parametersExt, matrixFactory, selectedConnectivityFactory);
acParameters.setDetailedReport(parametersExt.getReportedFeatures().contains(OpenLoadFlowParameters.ReportedFeatures.NEWTON_RAPHSON_LOAD_FLOW));
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Outer loops: {}", acParameters.getOuterLoops().stream().map(OuterLoop::getName).toList());
}
List results;
if (parametersExt.isNetworkCacheEnabled()) {
results = new AcLoadFlowFromCache(network, parameters, parametersExt, acParameters, reportNode)
.run();
} else {
try (LfNetworkList lfNetworkList = Networks.load(network, acParameters.getNetworkParameters(), new LfTopoConfig(), reportNode)) {
results = AcloadFlowEngine.run(lfNetworkList.getList(), acParameters);
}
}
// we reset the state if at least one component needs a network update.
boolean atLeastOneComponentHasToBeUpdated = results.stream().anyMatch(AcLoadFlowResult::isWithNetworkUpdate);
if (atLeastOneComponentHasToBeUpdated || parametersExt.isAlwaysUpdateNetwork()) {
Networks.resetState(network);
// reset slack buses if at least one component has converged
if (parameters.isWriteSlackBus()) {
SlackTerminal.reset(network);
}
// reset reference terminals if at least one component has converged
if (parametersExt.isWriteReferenceTerminals()) {
ReferenceTerminals.reset(network);
}
}
List componentResults = new ArrayList<>(results.size());
for (AcLoadFlowResult result : results) {
updateAcState(network, parameters, parametersExt, result, acParameters, atLeastOneComponentHasToBeUpdated);
ReferenceBusAndSlackBusesResults referenceBusAndSlackBusesResults = buildReferenceBusAndSlackBusesResults(result);
final var status = result.toComponentResultStatus();
componentResults.add(new LoadFlowResultImpl.ComponentResultImpl(result.getNetwork().getNumCC(),
result.getNetwork().getNumSC(),
status.status(),
status.statusText(),
Collections.emptyMap(), // metrics: can do better later on
result.getSolverIterations(),
referenceBusAndSlackBusesResults.referenceBusId(),
referenceBusAndSlackBusesResults.slackBusResultList(),
result.getDistributedActivePower() * PerUnit.SB));
}
boolean ok = results.stream().anyMatch(AcLoadFlowResult::isSuccess);
return new LoadFlowResultImpl(ok, Collections.emptyMap(), null, componentResults);
}
private static ReferenceBusAndSlackBusesResults buildReferenceBusAndSlackBusesResults(AbstractLoadFlowResult result) {
String referenceBusId = null;
List slackBusResultList = new ArrayList<>();
double slackBusActivePowerMismatch = result.getSlackBusActivePowerMismatch() * PerUnit.SB;
if (result.getNetwork().getValidity() == LfNetwork.Validity.VALID) {
referenceBusId = result.getNetwork().getReferenceBus().getId();
List slackBuses = result.getNetwork().getSlackBuses();
slackBusResultList = slackBuses.stream().map(
b -> (LoadFlowResult.SlackBusResult) new LoadFlowResultImpl.SlackBusResultImpl(b.getId(),
slackBusActivePowerMismatch / slackBuses.size())).toList();
}
return new ReferenceBusAndSlackBusesResults(referenceBusId, slackBusResultList);
}
private record ReferenceBusAndSlackBusesResults(String referenceBusId, List slackBusResultList) {
}
private void computeZeroImpedanceFlows(LfNetwork network, LoadFlowModel loadFlowModel) {
for (LfZeroImpedanceNetwork zeroImpedanceNetwork : network.getZeroImpedanceNetworks(loadFlowModel)) {
new ZeroImpedanceFlows(zeroImpedanceNetwork.getGraph(), zeroImpedanceNetwork.getSpanningTree(), loadFlowModel)
.compute();
}
}
private LoadFlowResult runDc(Network network, LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt, ReportNode reportNode) {
var dcParameters = OpenLoadFlowParameters.createDcParameters(network, parameters, parametersExt, matrixFactory, connectivityFactory, forcePhaseControlOffAndAddAngle1Var);
dcParameters.getNetworkParameters()
.setCacheEnabled(false); // force not caching as not supported in DC LF
List results = DcLoadFlowEngine.run(network, new LfNetworkLoaderImpl(), dcParameters, reportNode);
Networks.resetState(network);
List componentsResult = results.stream().map(r -> processResult(network, r, parameters, parametersExt, dcParameters.getNetworkParameters().isBreakers())).toList();
boolean ok = results.stream().anyMatch(DcLoadFlowResult::isSuccess);
return new LoadFlowResultImpl(ok, Collections.emptyMap(), null, componentsResult);
}
private LoadFlowResult.ComponentResult processResult(Network network, DcLoadFlowResult result, LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt, boolean breakers) {
if (result.isSuccess() && parameters.isWriteSlackBus()) {
SlackTerminal.reset(network);
}
if (result.isSuccess()) {
var updateParameters = new LfNetworkStateUpdateParameters(false,
parameters.isWriteSlackBus(),
parameters.isPhaseShifterRegulationOn(),
parameters.isTransformerVoltageControlOn(),
false,
false,
true,
breakers,
ReactivePowerDispatchMode.Q_EQUAL_PROPORTION,
parametersExt.isWriteReferenceTerminals(),
parametersExt.getReferenceBusSelectionMode(),
false);
result.getNetwork().updateState(updateParameters);
// zero or low impedance branch flows computation
computeZeroImpedanceFlows(result.getNetwork(), LoadFlowModel.DC);
}
var referenceBusAndSlackBusesResults = buildReferenceBusAndSlackBusesResults(result);
final var status = result.toComponentResultStatus();
return new LoadFlowResultImpl.ComponentResultImpl(
result.getNetwork().getNumCC(),
result.getNetwork().getNumSC(),
status.status(),
status.statusText(),
Collections.emptyMap(), // metrics: can do better later on
0, // iterationCount
referenceBusAndSlackBusesResults.referenceBusId(),
referenceBusAndSlackBusesResults.slackBusResultList(),
result.getDistributedActivePower() * PerUnit.SB);
}
@Override
public CompletableFuture run(Network network, ComputationManager computationManager, String workingVariantId, LoadFlowParameters parameters, ReportNode reportNode) {
Objects.requireNonNull(network);
Objects.requireNonNull(computationManager);
Objects.requireNonNull(workingVariantId);
Objects.requireNonNull(parameters);
Objects.requireNonNull(reportNode);
LOGGER.info("Version: {}", new PowsyblOpenLoadFlowVersion());
ReportNode lfReportNode = Reports.createLoadFlowReporter(reportNode, network.getId());
return CompletableFuture.supplyAsync(() -> {
network.getVariantManager().setWorkingVariant(workingVariantId);
Stopwatch stopwatch = Stopwatch.createStarted();
OpenLoadFlowParameters parametersExt = OpenLoadFlowParameters.get(parameters);
OpenLoadFlowParameters.log(parameters, parametersExt);
LoadFlowResult result = parameters.isDc() ? runDc(network, parameters, parametersExt, lfReportNode)
: runAc(network, parameters, parametersExt, lfReportNode);
stopwatch.stop();
LOGGER.info(Markers.PERFORMANCE_MARKER, "Load flow ran in {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return result;
}, computationManager.getExecutor());
}
@Override
public Optional getSpecificParametersSerializer() {
return Optional.of(new OpenLoadFlowParameterJsonSerializer());
}
@Override
public Optional> loadSpecificParameters(PlatformConfig platformConfig) {
return Optional.of(OpenLoadFlowParameters.load(platformConfig));
}
@Override
public Optional> loadSpecificParameters(Map properties) {
return Optional.of(OpenLoadFlowParameters.load(properties));
}
@Override
public List getSpecificParameters() {
return OpenLoadFlowParameters.SPECIFIC_PARAMETERS;
}
@Override
public void updateSpecificParameters(Extension extension, Map properties) {
((OpenLoadFlowParameters) extension).update(properties);
}
@Override
public Optional>> getSpecificParametersClass() {
return Optional.of(OpenLoadFlowParameters.class);
}
@Override
public Map createMapFromSpecificParameters(Extension extension) {
return ((OpenLoadFlowParameters) extension).toMap().entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> Objects.toString(e.getValue(), "")));
}
@Override
public Optional getModuleConfig(PlatformConfig platformConfig) {
return platformConfig.getOptionalModuleConfig(MODULE_SPECIFIC_PARAMETERS);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy