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

[WIP] HVDC AC emulation outerloop #1048

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7b3b61c
AcEmulationControl
Hadrien-Godard May 27, 2024
14ea87e
Continuing AcEmulationControl
Hadrien-Godard May 27, 2024
e334a54
Merge remote-tracking branch 'origin/main' into ac_emulation_outerloop
Hadrien-Godard Jun 3, 2024
5bee2ba
AcEmulationControl end
Hadrien-Godard Jun 3, 2024
2384e46
AcEmulationOuterLoop
Hadrien-Godard Jun 3, 2024
ec34c48
Merge remote-tracking branch 'origin/main' into ac_emulation_outerloop
Hadrien-Godard Jun 7, 2024
4cf7a20
HvdcAcEmulationSide1ActiveFlowEquationTerm
Hadrien-Godard Jun 7, 2024
52edfd9
Continuing outerloop
Hadrien-Godard Jun 7, 2024
2aaba91
End outerloop
Hadrien-Godard Jun 10, 2024
93d7de0
Merge branch 'main' into ac_emulation_outerloop
Hadrien-Godard Jun 14, 2024
448652a
Fix typos
Hadrien-Godard Jun 14, 2024
25ad3a8
Fix tests
Hadrien-Godard Jun 14, 2024
1c22515
Fix tests
Hadrien-Godard Jun 14, 2024
39bf64a
Reporter
Hadrien-Godard Jun 14, 2024
7e30920
Reporter test fix
Hadrien-Godard Jun 14, 2024
8b596e8
Fix
Hadrien-Godard Jun 14, 2024
2f44f84
Tests ok
Hadrien-Godard Jun 14, 2024
510192e
Fix
Hadrien-Godard Jun 14, 2024
eb70309
DcLoadFlow
Hadrien-Godard Jun 14, 2024
5897654
Refacto
Hadrien-Godard Jun 14, 2024
305a6a4
Test
Hadrien-Godard Jun 14, 2024
5d43d95
Listeners
Hadrien-Godard Jun 14, 2024
d295f9c
New test
Hadrien-Godard Jun 14, 2024
c174274
Merge branch 'main' into ac_emulation_outerloop
Hadrien-Godard Jun 24, 2024
198c4e1
Rename
Hadrien-Godard Jun 24, 2024
2521db6
Merge remote-tracking branch 'origin/main' into ac_emulation_outerloop
Hadrien-Godard Jun 24, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ protected static Optional<AcOuterLoop> createPhaseControlOuterLoop(LoadFlowParam
return createPhaseControlOuterLoop(parameters, parametersExt.getPhaseShifterControlMode());
}

protected static Optional<AcOuterLoop> createAcHvdcAcEmulationOuterLoop(LoadFlowParameters parameters) {
if (parameters.isHvdcAcEmulation()) {
return Optional.of(new AcHvdcAcEmulationOuterLoop());
}
return Optional.empty();
}

protected static Optional<AcOuterLoop> createAutomationSystemOuterLoop(OpenLoadFlowParameters parametersExt) {
if (parametersExt.isSimulateAutomationSystems()) {
return Optional.of(new AutomationSystemOuterLoop());
Expand Down
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);
// AC emulation
createAcHvdcAcEmulationOuterLoop(parameters).ifPresent(outerLoops::add);
// secondary voltage control
createSecondaryVoltageControlOuterLoop(parametersExt).ifPresent(outerLoops::add);
// primary voltage control
Expand Down
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,
AcHvdcAcEmulationOuterLoop.NAME);

private static Optional<AcOuterLoop> createOuterLoop(String name, LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt) {
return switch (name) {
Expand Down Expand Up @@ -65,6 +66,7 @@ private static Optional<AcOuterLoop> createOuterLoop(String name, LoadFlowParame
parametersExt.getGeneratorVoltageControlMinNominalVoltage());
case AutomationSystemOuterLoop.NAME -> createAutomationSystemOuterLoop(parametersExt);
case IncrementalTransformerReactivePowerControlOuterLoop.NAME -> createTransformerReactivePowerControlOuterLoop(parametersExt);
case AcHvdcAcEmulationOuterLoop.NAME -> createAcHvdcAcEmulationOuterLoop(parameters);
default -> throw new PowsyblException("Unknown outer loop '" + name + "'");
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,18 @@ protected AbstractHvdcAcEmulationFlowEquationTerm(LfHvdc hvdc, LfBus bus1, LfBus
ph1Var = variableSet.getVariable(bus1.getNum(), AcVariableType.BUS_PHI);
ph2Var = variableSet.getVariable(bus2.getNum(), AcVariableType.BUS_PHI);
variables = List.of(ph1Var, ph2Var);
k = hvdc.getDroop() * 180 / Math.PI;
p0 = hvdc.getP0();
k = hvdc.getAcEmulationControl().getDroop() * 180 / Math.PI;
p0 = hvdc.getAcEmulationControl().getP0();
lossFactor1 = hvdc.getConverterStation1().getLossFactor() / 100;
lossFactor2 = hvdc.getConverterStation2().getLossFactor() / 100;
pMaxFromCS1toCS2 = hvdc.getPMaxFromCS1toCS2();
pMaxFromCS2toCS1 = hvdc.getPMaxFromCS2toCS1();
pMaxFromCS1toCS2 = hvdc.getAcEmulationControl().getPMaxFromCS1toCS2();
pMaxFromCS2toCS1 = hvdc.getAcEmulationControl().getPMaxFromCS2toCS1();
}

protected double rawP(double p0, double k, double ph1, double ph2) {
return p0 + k * (ph1 - ph2);
}

protected double boundedP(double rawP) {
// If there is a maximal active power
// it is applied at the entry of the controller VSC station
// on the AC side of the network.
if (rawP >= 0) {
return Math.min(rawP, pMaxFromCS1toCS2);
} else {
return Math.max(rawP, -pMaxFromCS2toCS1);
}
}

protected double ph1() {
return sv.get(ph1Var.getRow());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
package com.powsybl.openloadflow.ac.equations;

import com.powsybl.iidm.network.TwoSides;
import com.powsybl.openloadflow.equations.Variable;
import com.powsybl.openloadflow.equations.VariableSet;
import com.powsybl.openloadflow.network.LfBus;
Expand All @@ -23,31 +24,29 @@ public HvdcAcEmulationSide1ActiveFlowEquationTerm(LfHvdc hvdc, LfBus bus1, LfBus
super(hvdc, bus1, bus2, variableSet);
}

private double p1(double ph1, double ph2) {
double rawP = rawP(p0, k, ph1, ph2);
double boundedP = boundedP(rawP);
return (isController(rawP) ? 1 : getVscLossMultiplier()) * boundedP;
}

private static boolean isController(double rawP) {
return rawP >= 0;
private double getSide1LossMultiplier() {
return element.getAcEmulationControl().getFeedingSide() == TwoSides.ONE ? 1 : getVscLossMultiplier();
}

private boolean isInOperatingRange(double rawP) {
return rawP < pMaxFromCS1toCS2 && rawP > -pMaxFromCS2toCS1;
private double p1(double ph1, double ph2) {
double boundedP = switch (element.getAcEmulationControl().getAcEmulationStatus()) {
case FREE -> rawP(p0, k, ph1, ph2);
case BOUNDED -> element.getAcEmulationControl().getFeedingSide() == TwoSides.ONE ? pMaxFromCS1toCS2 : -pMaxFromCS2toCS1;
default -> 0;
};
return getSide1LossMultiplier() * boundedP;
}

protected double dp1dph1(double ph1, double ph2) {
double rawP = rawP(p0, k, ph1, ph2);
if (isInOperatingRange(rawP)) {
return (isController(rawP) ? 1 : getVscLossMultiplier()) * k;
protected double dp1dph1() {
if (element.getAcEmulationControl().getAcEmulationStatus() == LfHvdc.AcEmulationControl.AcEmulationStatus.FREE) {
return getSide1LossMultiplier() * k;
} else {
return 0;
}
}

protected double dp1dph2(double ph1, double ph2) {
return -dp1dph1(ph1, ph2);
protected double dp1dph2() {
return -dp1dph1();
}

@Override
Expand All @@ -59,9 +58,9 @@ public double eval() {
public double der(Variable<AcVariableType> variable) {
Objects.requireNonNull(variable);
if (variable.equals(ph1Var)) {
return dp1dph1(ph1(), ph2());
return dp1dph1();
} else if (variable.equals(ph2Var)) {
return dp1dph2(ph1(), ph2());
return dp1dph2();
} else {
throw new IllegalStateException("Unknown variable: " + variable);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
package com.powsybl.openloadflow.ac.equations;

import com.powsybl.iidm.network.TwoSides;
import com.powsybl.openloadflow.equations.Variable;
import com.powsybl.openloadflow.equations.VariableSet;
import com.powsybl.openloadflow.network.LfBus;
Expand All @@ -23,31 +24,29 @@ public HvdcAcEmulationSide2ActiveFlowEquationTerm(LfHvdc hvdc, LfBus bus1, LfBus
super(hvdc, bus1, bus2, variableSet);
}

private double p2(double ph1, double ph2) {
double rawP = rawP(p0, k, ph1, ph2);
double boundedP = boundedP(rawP);
return -(isController(rawP) ? 1 : getVscLossMultiplier()) * boundedP;
}

private boolean isController(double rawP) {
return rawP < 0;
private double getSide2LossMultiplier() {
return element.getAcEmulationControl().getFeedingSide() == TwoSides.TWO ? 1 : getVscLossMultiplier();
}

private boolean isInOperatingRange(double rawP) {
return rawP < pMaxFromCS2toCS1 && rawP > -pMaxFromCS1toCS2;
private double p2(double ph1, double ph2) {
double boundedP = switch (element.getAcEmulationControl().getAcEmulationStatus()) {
case FREE -> -rawP(p0, k, ph1, ph2);
case BOUNDED -> element.getAcEmulationControl().getFeedingSide() == TwoSides.TWO ? pMaxFromCS2toCS1 : -pMaxFromCS1toCS2;
default -> 0;
};
return getSide2LossMultiplier() * boundedP;
}

private double dp2dph1(double ph1, double ph2) {
double rawP = rawP(p0, k, ph1, ph2);
if (isInOperatingRange(rawP)) {
return -(isController(rawP) ? 1 : getVscLossMultiplier()) * k;
private double dp2dph1() {
if (element.getAcEmulationControl().getAcEmulationStatus() == LfHvdc.AcEmulationControl.AcEmulationStatus.FREE) {
return -getSide2LossMultiplier() * k;
} else {
return 0;
}
}

private double dp2dph2(double ph1, double ph2) {
return -dp2dph1(ph1, ph2);
private double dp2dph2() {
return -dp2dph1();
}

@Override
Expand All @@ -59,9 +58,9 @@ public double eval() {
public double der(Variable<AcVariableType> variable) {
Objects.requireNonNull(variable);
if (variable.equals(ph1Var)) {
return dp2dph1(ph1(), ph2());
return dp2dph1();
} else if (variable.equals(ph2Var)) {
return dp2dph2(ph1(), ph2());
return dp2dph2();
} else {
throw new IllegalStateException("Unknown variable: " + variable);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* Copyright (c) 2023, 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.commons.report.ReportNode;
import com.powsybl.iidm.network.TwoSides;
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.AbstractHvdcAcEmulationOuterLoop;
import com.powsybl.openloadflow.lf.outerloop.OuterLoopResult;
import com.powsybl.openloadflow.lf.outerloop.OuterLoopStatus;
import com.powsybl.openloadflow.network.LfHvdc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Hadrien Godard {@literal <hadrien.godard at artelys.com>}
*/
public class AcHvdcAcEmulationOuterLoop
extends AbstractHvdcAcEmulationOuterLoop<AcVariableType, AcEquationType, AcLoadFlowParameters, AcLoadFlowContext, AcOuterLoopContext>
implements AcOuterLoop {

private static final Logger LOGGER = LoggerFactory.getLogger(AcHvdcAcEmulationOuterLoop.class);
public static final String NAME = "AcHvdcAcEmulation";

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

private boolean checkFeedingSide(LfHvdc hvdc, ContextData contextData) {
String hvdcId = hvdc.getId();
LfHvdc.AcEmulationControl acEmulationControl = hvdc.getAcEmulationControl();

if (acEmulationControl.getFeedingSide() == TwoSides.ONE) {
if (hvdc.getP1().eval() < 0) {
// Switch feeding side
LOGGER.trace("Switching feeding side from One to Two for Hvdc: {}", hvdcId);
contextData.incrementFeedingSideSwitchCount(hvdcId);
hvdc.updateFeedingSide(TwoSides.TWO);
if (contextData.getFeedingSideSwitchCount(hvdcId) == MAX_FEEDING_SIDE_SWITCH) {
LOGGER.debug("Two many feeding side switches (flow blocked to 0 MW) for Hvdc: {}", hvdcId);
hvdc.updateAcEmulationStatus(LfHvdc.AcEmulationControl.AcEmulationStatus.NULL);
}
return true;
}
} else {
if (hvdc.getP2().eval() < 0) {
// Switch feeding side
LOGGER.trace("Switching feeding side from Two to One for Hvdc: {}", hvdcId);
contextData.incrementFeedingSideSwitchCount(hvdcId);
hvdc.updateFeedingSide(TwoSides.ONE);
if (contextData.getFeedingSideSwitchCount(hvdcId) == MAX_FEEDING_SIDE_SWITCH) {
LOGGER.debug("Two many feeding side switches (flow blocked to 0 MW) for Hvdc: {}", hvdcId);
hvdc.updateAcEmulationStatus(LfHvdc.AcEmulationControl.AcEmulationStatus.NULL);
}
return true;
}
}
return false;
}

private boolean checkMode(LfHvdc hvdc, ContextData contextData) {
String hvdcId = hvdc.getId();
LfHvdc.AcEmulationControl acEmulationControl = hvdc.getAcEmulationControl();

// Check for mode switch between FREE and BOUNDED
if (acEmulationControl.getAcEmulationStatus() == LfHvdc.AcEmulationControl.AcEmulationStatus.FREE) {
// Check Pmax
if (acEmulationControl.getFeedingSide() == TwoSides.ONE) {
if (hvdc.getP1().eval() > acEmulationControl.getPMaxFromCS1toCS2()) {
// Switch mode
LOGGER.trace("Bound Hvdc flow to Pmax from CS1 to CS2 for Hvdc: {}", hvdcId);
contextData.incrementModeSwitchCount(hvdcId);
hvdc.updateAcEmulationStatus(LfHvdc.AcEmulationControl.AcEmulationStatus.BOUNDED);
if (contextData.getModeSwitchCount(hvdcId) == MAX_MODE_SWITCH) {
LOGGER.debug("Two many mode switches (flow blocked to Pmax from CS1 to CS2) for Hvdc: {}", hvdcId);
}
return true;
}
} else {
if (hvdc.getP2().eval() > acEmulationControl.getPMaxFromCS2toCS1()) {
// Switch mode
LOGGER.trace("Bound Hvdc flow to Pmax from CS2 to CS1 for Hvdc: {}", hvdcId);
contextData.incrementModeSwitchCount(hvdcId);
hvdc.updateAcEmulationStatus(LfHvdc.AcEmulationControl.AcEmulationStatus.BOUNDED);
if (contextData.getModeSwitchCount(hvdcId) == MAX_MODE_SWITCH) {
LOGGER.debug("Two many mode switches (flow blocked to Pmax from CS2 to CS1) for Hvdc: {}", hvdcId);
}
return true;
}
}
}

// Check for mode switch between BOUNDED and FREE
if (acEmulationControl.getAcEmulationStatus() == LfHvdc.AcEmulationControl.AcEmulationStatus.BOUNDED) {
if (acEmulationControl.getFeedingSide() == TwoSides.ONE) {
if (computeRawP1(hvdc) < acEmulationControl.getPMaxFromCS1toCS2()) {
// Switch mode
LOGGER.trace("Set free the Ac Emulation mode for Hvdc: {}", hvdcId);
hvdc.updateAcEmulationStatus(LfHvdc.AcEmulationControl.AcEmulationStatus.FREE);
return true;
}
} else {
if (computeRawP2(hvdc) < acEmulationControl.getPMaxFromCS2toCS1()) {
// Switch mode
LOGGER.trace("Set free the Ac Emulation mode for Hvdc: {}", hvdcId);
hvdc.updateAcEmulationStatus(LfHvdc.AcEmulationControl.AcEmulationStatus.FREE);
return true;
}
}
}
return false;
}

@Override
public OuterLoopResult check(AcOuterLoopContext context, ReportNode reportNode) {
OuterLoopStatus status = OuterLoopStatus.STABLE;
ContextData contextData = (ContextData) context.getData();

for (LfHvdc hvdc : context.getNetwork().getHvdcs()) {
if (!hvdc.isAcEmulation() || hvdc.getBus1().isDisabled() || hvdc.getBus2().isDisabled() || hvdc.isDisabled()) {
continue;
}
String hvdcId = hvdc.getId();
if (contextData.getFeedingSideSwitchCount(hvdcId) < MAX_FEEDING_SIDE_SWITCH && contextData.getModeSwitchCount(hvdcId) < MAX_MODE_SWITCH) {
// First check the feeding side
if (checkFeedingSide(hvdc, contextData)) {
status = OuterLoopStatus.UNSTABLE;
}

// Second check for Pmax values
if (checkMode(hvdc, contextData)) {
status = OuterLoopStatus.UNSTABLE;
}
}
}

return new OuterLoopResult(this, status);
}
}
Loading