Skip to content

Commit

Permalink
Area Interchange Control as OuterLoop (#1055)
Browse files Browse the repository at this point in the history
Signed-off-by: mguibert <marine.guibert@artelys.com>
Co-authored-by: vmouradian <valentin.mouradian@artelys.com>
  • Loading branch information
m-guibert and vmouradian authored Oct 9, 2024
1 parent 5e3d7b1 commit e3bf408
Show file tree
Hide file tree
Showing 32 changed files with 1,790 additions and 35 deletions.
78 changes: 78 additions & 0 deletions docs/loadflow/loadflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,81 @@ Note that the vector $b$ of right-hand sides is linearly computed from the given

To solve this system, we follow the classic approach of the LU matrices decomposition $J = LU$.
Hence, by solving the system using LU decomposition, you can compute the voltage angles by giving as data the injections and the phase-shifting angles.

## 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.

The area interchange control feature is optional, can be activated via the [parameter `areaInterchangeControl`](parameters.md)
and is performed by an outer loop.

Area Interchange Control is performed using an outer loop, similar in principle to the traditional `SlackDistribution` outer loop.
However unlike the `SlackDistribution` outer loop which distributes imbalance over the entire synchronous component (island),
the Area Interchange Control outer loop performs an active power distribution over areas
(filtered on areas having their type matching the configured [parameter `areaInterchangeControlAreaType`](parameters.md)),
in order to have all areas' active power interchanges matching their target interchanges.

The Area Interchange Control outer loop can handle networks where part (or even all) of the buses are not in an area.
For networks that have no areas at all, the behaviour is the same as with the distributed slack outer loop - in such case
internally the Area Interchange Control outer loop just triggers the Slack Distribution outer loop logic.

Just like other outer loops, the Area Interchange Control outer loop checks whether area imbalance must be distributed:
* If no, the outer loop is stable
* If yes, the outer loop is unstable and a new Newton-Raphson is triggered

### Area Interchange Control - algorithm description

The active power is distributed separately on injections (as configured in the [parameter `balanceType`](parameters.md)) of each area
to compensate the area "total mismatch" that is given by:

$$
Area Total Mismatch = Interchange - Interchange Target + Slack Injection
$$

Where:
* "Interchange" is the sum of the power flows at the boundaries of the area (load sign convention i.e. counted positive for imports).
* "Interchange Target" is the interchange target parameter of the area.
* "Slack Injection" is the active power mismatch of the slack bus(es) present in the area (see [Slack bus mismatch attribution](#slack-bus-mismatch-attribution)).

The outer loop iterates until this mismatch is below the configured [parameter `areaInterchangePMaxMismatch`](parameters.md) for all areas.

When it is the case, "interchange only" mismatch is computed for all areas:

$$
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 not, the remaining mismatch is first distributed over the buses that have no area.

If some 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:
- Areas without interchange target
- Areas without boundaries
- Areas that have boundaries in multiple synchronous/connected components. If all the boundaries are in the same component but some buses are in different components, only the part in the component of the boundaries will be considered.

In such cases the involved areas are not considered in the Area Interchange Control outer loop, however other valid areas will still be considered.

### Interchange flow calculation

In iIDM each area defines the boundary points to be considered in the interchange. iIDM supports two ways of modeling area boundaries:
- either via an equipment terminal,
- or via a DanglingLine boundary.

In the DanglingLine case, the flow at the boundary side is considered as it should be, for both unpaired DanglingLines and DanglingLines paired in a TieLine.

### Slack bus mismatch attribution
Depending on the location of the slack bus(es), the role of distributing the active power mismatch will be attributed based on the following logic:
- If the slack bus is part of an area: the slack power is attributed to the area (see "total mismatch" calculation in [Algorithm description](#area-interchange-control---algorithm-description)).
Indeed, in this case the slack injection can be seen as an interchange to 'the void' which must be resolved.
- Slack bus has no area:
- Connected to other bus(es) without area: treated as the slack mismatch of the buses without area
- Connected to only buses that have an area:
- All connected branches are boundaries of those areas: Not attributed to anyone, the mismatch will already be present in the interchange mismatch
- Some connected branches are not declared as boundaries of the areas: Amount of mismatch to distribute is split equally among the areas (added to their "total mismatch")
14 changes: 14 additions & 0 deletions docs/loadflow/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,20 @@ When slack distribution is enabled (`distributedSlack` set to `true` in LoadFlow
is considered to be distributed.
The default value is `1 MW` and it must be greater or equal to `0 MW`.

**areaInterchangeControl**
The `areaInterchangeControl` property is an optional property that defines if the [area interchange control](loadflow.md#area-interchange-control) outer loop is enabled.
If set to `true`, the area interchange control outer loop will be used instead of the slack distribution outer loop.
The default value is `false`.

**areaInterchangeControlAreaType**
Defines the `areaType` of the areas on which the [area interchange control](loadflow.md#area-interchange-control) is applied.
Only the areas of the input network that have this type will be considered.
The default value is `ControlArea`.

**areaInterchangePMaxMismatch**
Defines the maximum interchange mismatch tolerance for [area interchange control](loadflow.md#area-interchange-control).
The default value is `2 MW` and it must be greater than `0 MW`.

**voltageRemoteControl**
The `voltageRemoteControl` property is an optional property that defines if the remote control for voltage controllers has to be modeled.
If set to false, any existing voltage remote control is converted to a local control, rescaling the target voltage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,19 @@
import com.powsybl.openloadflow.ac.outerloop.*;
import com.powsybl.openloadflow.network.util.ActivePowerDistribution;
import com.powsybl.openloadflow.util.PerUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Optional;

/**
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
abstract class AbstractAcOuterLoopConfig implements AcOuterLoopConfig {

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

protected AbstractAcOuterLoopConfig() {
}

Expand All @@ -30,6 +35,14 @@ protected static Optional<AcOuterLoop> createDistributedSlackOuterLoop(LoadFlowP
return Optional.empty();
}

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.empty();
}

protected static Optional<AcOuterLoop> createReactiveLimitsOuterLoop(LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt) {
if (parameters.isUseReactiveLimits()) {
double effectiveMaxReactivePowerMismatch = switch (parametersExt.getNewtonRaphsonStoppingCriteriaType()) {
Expand Down Expand Up @@ -122,4 +135,18 @@ protected static Optional<AcOuterLoop> createAutomationSystemOuterLoop(OpenLoadF
}
return Optional.empty();
}

static List<AcOuterLoop> filterInconsistentOuterLoops(List<AcOuterLoop> outerLoops) {
if (outerLoops.stream().anyMatch(AreaInterchangeControlOuterloop.class::isInstance)) {
return outerLoops.stream().filter(o -> {
if (o instanceof DistributedSlackOuterLoop) {
LOGGER.warn("Distributed slack and area interchange control are both enabled. " +
"Distributed slack outer loop will be disabled, slack will be distributed by the area interchange control.");
return false;
}
return true;
}).toList();
}
return outerLoops;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public List<AcOuterLoop> configure(LoadFlowParameters parameters, OpenLoadFlowPa
List<AcOuterLoop> outerLoops = new ArrayList<>(5);
// primary frequency control
createDistributedSlackOuterLoop(parameters, parametersExt).ifPresent(outerLoops::add);
// area interchange control
createAreaInterchangeControlOuterLoop(parameters, parametersExt).ifPresent(outerLoops::add);
// secondary voltage control
createSecondaryVoltageControlOuterLoop(parametersExt).ifPresent(outerLoops::add);
// primary voltage control
Expand All @@ -38,6 +40,6 @@ public List<AcOuterLoop> configure(LoadFlowParameters parameters, OpenLoadFlowPa
createShuntVoltageControlOuterLoop(parameters, parametersExt).ifPresent(outerLoops::add);
// automation system
createAutomationSystemOuterLoop(parametersExt).ifPresent(outerLoops::add);
return outerLoops;
return filterInconsistentOuterLoops(outerLoops);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public class ExplicitAcOuterLoopConfig extends AbstractAcOuterLoopConfig {
SimpleTransformerVoltageControlOuterLoop.NAME,
TransformerVoltageControlOuterLoop.NAME,
AutomationSystemOuterLoop.NAME,
IncrementalTransformerReactivePowerControlOuterLoop.NAME);
IncrementalTransformerReactivePowerControlOuterLoop.NAME,
AreaInterchangeControlOuterloop.NAME);

private static Optional<AcOuterLoop> createOuterLoop(String name, LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt) {
return switch (name) {
Expand Down Expand Up @@ -68,6 +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);
default -> throw new PowsyblException("Unknown outer loop '" + name + "'");
};
}
Expand All @@ -89,6 +91,6 @@ public List<AcOuterLoop> configure(LoadFlowParameters parameters, OpenLoadFlowPa
.flatMap(name -> createOuterLoop(name, parameters, parametersExt).stream())
.toList();
checkTypeUnicity(outerLoops);
return outerLoops;
return filterInconsistentOuterLoops(outerLoops);
}
}
Loading

0 comments on commit e3bf408

Please sign in to comment.