Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DC Area Interchange Control OuterLoop #1123

Merged
merged 17 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions docs/loadflow/loadflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,7 @@ Hence, by solving the system using LU decomposition, you can compute the voltage

## Area Interchange Control

Area Interchange Control consists in having the Load Flow finding a solution where area interchanges are solved to match the input target interchange values.

Currently, Area Interchange Control is only supported for AC load flow, DC load flow support is planned for future release.
Area Interchange Control consists in having the Load Flow finding a solution where area interchanges are solved to match the input target interchange values. It is supported for both AC and DC Load Flow computations.

The area interchange control feature is optional, can be activated via the [parameter `areaInterchangeControl`](parameters.md)
and is performed by an outer loop.
Expand Down Expand Up @@ -247,12 +245,11 @@ $$
Interchange Mismatch = Interchange - Interchange Target
$$

If this mismatch for all areas and the slack injection of the buses without area are below the configured [parameter `slackBusPMaxMismatch`](parameters.md)
then the outerloop is stable and declares a stable status, meaning that the interchanges are correct and the slack bus active power is distributed.
If this mismatch for all areas and the slack injection of the buses without area are below the configured [parameter `slackBusPMaxMismatch`](parameters.md), then the outer loop declares a stable status, meaning that the interchanges are correct and the slack bus active power is distributed.

If not, the remaining mismatch is first distributed over the buses that have no area.
If not, the remaining slack bus mismatch is first distributed over the buses that have no area.

If some mismatch still remains, it is distributed equally over all the areas.
If some slack bus mismatch still remains, it is distributed equally over all the areas.

### Areas validation
There are some cases where areas are considered invalid and will not be considered for the area interchange control:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected static Optional<AcOuterLoop> createDistributedSlackOuterLoop(LoadFlowP
protected static Optional<AcOuterLoop> createAreaInterchangeControlOuterLoop(LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt) {
if (parametersExt.isAreaInterchangeControl()) {
ActivePowerDistribution activePowerDistribution = ActivePowerDistribution.create(parameters.getBalanceType(), parametersExt.isLoadPowerFactorConstant(), parametersExt.isUseActiveLimits());
return Optional.of(new AreaInterchangeControlOuterloop(activePowerDistribution, parametersExt.getSlackBusPMaxMismatch(), parametersExt.getAreaInterchangePMaxMismatch()));
return Optional.of(new AcAreaInterchangeControlOuterLoop(activePowerDistribution, parametersExt.getSlackBusPMaxMismatch(), parametersExt.getAreaInterchangePMaxMismatch()));
}
return Optional.empty();
}
Expand Down Expand Up @@ -137,7 +137,7 @@ protected static Optional<AcOuterLoop> createAutomationSystemOuterLoop(OpenLoadF
}

static List<AcOuterLoop> filterInconsistentOuterLoops(List<AcOuterLoop> outerLoops) {
if (outerLoops.stream().anyMatch(AreaInterchangeControlOuterloop.class::isInstance)) {
if (outerLoops.stream().anyMatch(AcAreaInterchangeControlOuterLoop.class::isInstance)) {
return outerLoops.stream().filter(o -> {
if (o instanceof DistributedSlackOuterLoop) {
LOGGER.warn("Distributed slack and area interchange control are both enabled. " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class ExplicitAcOuterLoopConfig extends AbstractAcOuterLoopConfig {
TransformerVoltageControlOuterLoop.NAME,
AutomationSystemOuterLoop.NAME,
IncrementalTransformerReactivePowerControlOuterLoop.NAME,
AreaInterchangeControlOuterloop.NAME);
AcAreaInterchangeControlOuterLoop.NAME);

private static Optional<AcOuterLoop> createOuterLoop(String name, LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt) {
return switch (name) {
Expand Down Expand Up @@ -69,7 +69,7 @@ private static Optional<AcOuterLoop> createOuterLoop(String name, LoadFlowParame
parametersExt.getGeneratorVoltageControlMinNominalVoltage());
case AutomationSystemOuterLoop.NAME -> createAutomationSystemOuterLoop(parametersExt);
case IncrementalTransformerReactivePowerControlOuterLoop.NAME -> createTransformerReactivePowerControlOuterLoop(parametersExt);
case AreaInterchangeControlOuterloop.NAME -> createAreaInterchangeControlOuterLoop(parameters, parametersExt);
case AcAreaInterchangeControlOuterLoop.NAME -> createAreaInterchangeControlOuterLoop(parameters, parametersExt);
default -> throw new PowsyblException("Unknown outer loop '" + name + "'");
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1923,7 +1923,10 @@ public static DcLoadFlowParameters createDcParameters(LoadFlowParameters paramet
.setSvcVoltageMonitoring(false)
.setMaxSlackBusCount(parametersExt.getMaxSlackBusCount())
.setLinePerUnitMode(parametersExt.getLinePerUnitMode())
.setReferenceBusSelector(ReferenceBusSelector.fromMode(parametersExt.getReferenceBusSelectionMode()));
.setLinePerUnitMode(parametersExt.getLinePerUnitMode())
m-guibert marked this conversation as resolved.
Show resolved Hide resolved
.setReferenceBusSelector(ReferenceBusSelector.fromMode(parametersExt.getReferenceBusSelectionMode()))
.setAreaInterchangeControl(parametersExt.isAreaInterchangeControl())
.setAreaInterchangeControlAreaType(parametersExt.getAreaInterchangeControlAreaType());

var equationSystemCreationParameters = new DcEquationSystemCreationParameters()
.setUpdateFlows(true)
Expand All @@ -1935,11 +1938,15 @@ public static DcLoadFlowParameters createDcParameters(LoadFlowParameters paramet
return new DcLoadFlowParameters()
.setNetworkParameters(networkParameters)
.setEquationSystemCreationParameters(equationSystemCreationParameters)
.setSlackDistributionFailureBehavior(parametersExt.getSlackDistributionFailureBehavior())
.setMatrixFactory(matrixFactory)
.setDistributedSlack(parameters.isDistributedSlack())
.setAreaInterchangeControl(parametersExt.isAreaInterchangeControl())
.setBalanceType(parameters.getBalanceType())
.setSetVToNan(true)
.setMaxOuterLoopIterations(parametersExt.getMaxOuterLoopIterations());
.setMaxOuterLoopIterations(parametersExt.getMaxOuterLoopIterations())
.setSlackBusPMaxMismatch(parametersExt.getSlackBusPMaxMismatch())
vidaldid-rte marked this conversation as resolved.
Show resolved Hide resolved
.setAreaInterchangePMaxMismatch(parametersExt.getAreaInterchangePMaxMismatch());
}

public static boolean equals(LoadFlowParameters parameters1, LoadFlowParameters parameters2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ private LoadFlowResult.ComponentResult processResult(Network network, DcLoadFlow
0, // iterationCount
referenceBusAndSlackBusesResults.referenceBusId(),
referenceBusAndSlackBusesResults.slackBusResultList(),
Double.NaN);
result.getDistributedActivePower() * PerUnit.SB);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/
package com.powsybl.openloadflow.ac;

import com.powsybl.openloadflow.OpenLoadFlowParameters;
import com.powsybl.openloadflow.ac.equations.AcEquationSystemCreationParameters;
import com.powsybl.openloadflow.ac.outerloop.AcOuterLoop;
import com.powsybl.openloadflow.ac.solver.AcSolverFactory;
Expand Down Expand Up @@ -42,8 +41,6 @@ public class AcLoadFlowParameters extends AbstractLoadFlowParameters<AcLoadFlowP

private boolean asymmetrical = LfNetworkParameters.ASYMMETRICAL_DEFAULT_VALUE;

private OpenLoadFlowParameters.SlackDistributionFailureBehavior slackDistributionFailureBehavior = OpenLoadFlowParameters.SlackDistributionFailureBehavior.LEAVE_ON_SLACK_BUS;

private AcSolverFactory solverFactory = new NewtonRaphsonFactory();

private boolean detailedReport = false;
Expand Down Expand Up @@ -111,15 +108,6 @@ public AcLoadFlowParameters setAsymmetrical(boolean asymmetrical) {
return this;
}

public OpenLoadFlowParameters.SlackDistributionFailureBehavior getSlackDistributionFailureBehavior() {
return slackDistributionFailureBehavior;
}

public AcLoadFlowParameters setSlackDistributionFailureBehavior(OpenLoadFlowParameters.SlackDistributionFailureBehavior slackDistributionFailureBehavior) {
this.slackDistributionFailureBehavior = Objects.requireNonNull(slackDistributionFailureBehavior);
return this;
}

public AcSolverFactory getSolverFactory() {
return solverFactory;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,12 @@ public static AcLoadFlowResult createNoCalculationResult(LfNetwork network) {

private final AcSolverStatus solverStatus;

private final double distributedActivePower;

public AcLoadFlowResult(LfNetwork network, int outerLoopIterations, int solverIterations,
AcSolverStatus solverStatus, OuterLoopResult outerLoopResult,
double slackBusActivePowerMismatch, double distributedActivePower) {
super(network, slackBusActivePowerMismatch, outerLoopIterations, outerLoopResult);
super(network, slackBusActivePowerMismatch, outerLoopIterations, outerLoopResult, distributedActivePower);
this.solverIterations = solverIterations;
this.solverStatus = Objects.requireNonNull(solverStatus);
this.distributedActivePower = distributedActivePower;
}

public int getSolverIterations() {
Expand All @@ -49,10 +46,6 @@ public AcSolverStatus getSolverStatus() {
return solverStatus;
}

public double getDistributedActivePower() {
return distributedActivePower;
}

@Override
public boolean isSuccess() {
return solverStatus == AcSolverStatus.CONVERGED && getOuterLoopResult().status() == OuterLoopStatus.STABLE;
Expand Down
10 changes: 4 additions & 6 deletions src/main/java/com/powsybl/openloadflow/ac/AcloadFlowEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@
import com.powsybl.commons.report.ReportNode;
import com.powsybl.openloadflow.ac.equations.AcEquationType;
import com.powsybl.openloadflow.ac.equations.AcVariableType;
import com.powsybl.openloadflow.ac.outerloop.AcActivePowerDistributionOuterLoop;
import com.powsybl.openloadflow.ac.outerloop.AcOuterLoop;
import com.powsybl.openloadflow.ac.outerloop.AreaInterchangeControlOuterloop;
import com.powsybl.openloadflow.ac.outerloop.DistributedSlackOuterLoop;
import com.powsybl.openloadflow.ac.solver.*;
import com.powsybl.openloadflow.lf.LoadFlowEngine;
import com.powsybl.openloadflow.lf.outerloop.DistributedSlackContextData;
import com.powsybl.openloadflow.lf.outerloop.OuterLoopResult;
import com.powsybl.openloadflow.lf.outerloop.OuterLoopStatus;
import com.powsybl.openloadflow.network.LfNetwork;
Expand Down Expand Up @@ -209,8 +207,8 @@ public AcLoadFlowResult run() {
for (var outerLoopAndContext : Lists.reverse(outerLoopsAndContexts)) {
var outerLoop = outerLoopAndContext.getLeft();
var outerLoopContext = outerLoopAndContext.getRight();
if (outerLoop instanceof DistributedSlackOuterLoop || outerLoop instanceof AreaInterchangeControlOuterloop) {
distributedActivePower = ((DistributedSlackContextData) outerLoopContext.getData()).getDistributedActivePower();
if (outerLoop instanceof AcActivePowerDistributionOuterLoop activePowerDistributionOuterLoop) {
distributedActivePower = activePowerDistributionOuterLoop.getDistributedActivePower(outerLoopContext);
}
outerLoop.cleanup(outerLoopContext);
}
Expand All @@ -237,7 +235,7 @@ private AcLoadFlowResult buildAcLoadFlowResult(RunningContext runningContext, Ou
distributedActivePower
);

LOGGER.info("Ac loadflow complete on network {} (result={})", context.getNetwork(), result);
LOGGER.info("AC loadflow complete on network {} (result={})", context.getNetwork(), result);

Reports.reportAcLfComplete(context.getNetwork().getReportNode(), result.isSuccess(), result.getSolverStatus().name(), result.getOuterLoopResult().status().name());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/)
* 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.ac.outerloop;

import com.powsybl.openloadflow.ac.AcLoadFlowContext;
import com.powsybl.openloadflow.ac.AcLoadFlowParameters;
import com.powsybl.openloadflow.ac.AcOuterLoopContext;
import com.powsybl.openloadflow.ac.equations.AcEquationType;
import com.powsybl.openloadflow.ac.equations.AcVariableType;
import com.powsybl.openloadflow.lf.outerloop.ActivePowerDistributionOuterLoop;

/**
* @author Valentin Mouradian {@literal <valentin.mouradian at artelys.com>}
*/
public interface AcActivePowerDistributionOuterLoop extends ActivePowerDistributionOuterLoop<AcVariableType, AcEquationType, AcLoadFlowParameters, AcLoadFlowContext, AcOuterLoopContext> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/)
* 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.ac.outerloop;

import com.powsybl.openloadflow.ac.AcLoadFlowContext;
import com.powsybl.openloadflow.ac.AcLoadFlowParameters;
import com.powsybl.openloadflow.ac.AcOuterLoopContext;
import com.powsybl.openloadflow.ac.equations.AcEquationType;
import com.powsybl.openloadflow.ac.equations.AcVariableType;
import com.powsybl.openloadflow.lf.outerloop.AbstractAreaInterchangeControlOuterLoop;
import com.powsybl.openloadflow.network.util.ActivePowerDistribution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Valentin Mouradian {@literal <valentin.mouradian at artelys.com>}
*/
public class AcAreaInterchangeControlOuterLoop
extends AbstractAreaInterchangeControlOuterLoop<AcVariableType, AcEquationType, AcLoadFlowParameters, AcLoadFlowContext, AcOuterLoopContext>
implements AcOuterLoop, AcActivePowerDistributionOuterLoop {

private static final Logger LOGGER = LoggerFactory.getLogger(AcAreaInterchangeControlOuterLoop.class);

public static final String NAME = "AreaInterchangeControl";
m-guibert marked this conversation as resolved.
Show resolved Hide resolved

public AcAreaInterchangeControlOuterLoop(ActivePowerDistribution activePowerDistribution, double slackBusPMaxMismatch, double areaInterchangePMaxMismatch) {
super(activePowerDistribution, new DistributedSlackOuterLoop(activePowerDistribution, slackBusPMaxMismatch), slackBusPMaxMismatch, areaInterchangePMaxMismatch, LOGGER);
}

@Override
public String getName() {
return NAME;
}

@Override
public double getSlackBusActivePowerMismatch(AcOuterLoopContext context) {
return context.getLastSolverResult().getSlackBusActivePowerMismatch();
}

}
Loading