-
Notifications
You must be signed in to change notification settings - Fork 11
[WIP] HVDC AC emulation outerloop #1048
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
base: main
Are you sure you want to change the base?
Changes from 23 commits
7b3b61c
14ea87e
e334a54
5bee2ba
2384e46
ec34c48
4cf7a20
52edfd9
2aaba91
93d7de0
448652a
25ad3a8
1c22515
39bf64a
7e30920
8b596e8
2f44f84
510192e
eb70309
5897654
305a6a4
5d43d95
d295f9c
c174274
198c4e1
2521db6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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.AbstractAcEmulationOuterLoop; | ||
| 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 AcEmulationOuterLoop | ||
| extends AbstractAcEmulationOuterLoop<AcVariableType, AcEquationType, AcLoadFlowParameters, AcLoadFlowContext, AcOuterLoopContext> | ||
Hadrien-Godard marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| implements AcOuterLoop { | ||
|
|
||
| private static final Logger LOGGER = LoggerFactory.getLogger(AcEmulationOuterLoop.class); | ||
| public static final String NAME = "AcEmulation"; | ||
|
|
||
| @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); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a problem with this NULL status. For example the flip fop may result from changes of Phase Tap changers, or other automates, that changes the active power routes. It is not necessarily that the active power in the HVDC has small oscillations around 0 and can be neglected. Have you found examples where small endless oscillations happen here ? Wouldn't a dead angle zone for which P is 0 be better ?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea here is that, if the HVDC is changing its feeding side multiple times during the LF algorithm, then its flow should be very close to 0. In realistic case, the feeding side should never change at all during iterations. We do not have any cases where endless oscillations around zero occur, but in theory, without this security, it may happen. It is mainly because of the discontinuity of the derivative from k to -k around 0. |
||
| 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; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What you really want to know here is whether the hvdc link is active. That is if the hvdc in AC emulation mode and the P1 and P2 equations terms are active.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok I will try that |
||
| } | ||
| 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); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.