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

Support of condensers #3065

Merged
merged 6 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
28 changes: 16 additions & 12 deletions docs/grid_model/network_subnetwork.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,18 +159,19 @@ A generator is an equipment that injects or consumes active power, and injects o

**Characteristics**

| Attribute | Unit | Description |
| --------- | ---- |-------------------------------------------------------------|
| $MinP$ | MW | Minimum generator active power output |
| $MaxP$ | MW | Maximum generator active power output |
| $ReactiveLimits$ | MVar | Operational limits of the generator (P/Q/V diagram) |
| $RatedS$ | MVA | The rated nominal power |
| $TargetP$ | MW | The active power target |
| $TargetQ$ | MVAr | The reactive power target at local terminal |
| $TargetV$ | kV | The voltage target at regulating terminal |
| $RegulatingTerminal$ | | Associated node or bus for which voltage is to be regulated |
| $VoltageRegulatorOn$ | | True if the generator regulates voltage |
| $EnergySource$ | | The energy source harnessed to turn the generator |
| Attribute | Unit | Description |
|----------------------|------|-------------------------------------------------------------|
| $MinP$ | MW | Minimum generator active power output |
| $MaxP$ | MW | Maximum generator active power output |
| $ReactiveLimits$ | MVar | Operational limits of the generator (P/Q/V diagram) |
| $RatedS$ | MVA | The rated nominal power |
| $TargetP$ | MW | The active power target |
| $TargetQ$ | MVAr | The reactive power target at local terminal |
| $TargetV$ | kV | The voltage target at regulating terminal |
| $RegulatingTerminal$ | | Associated node or bus for which voltage is to be regulated |
| $VoltageRegulatorOn$ | | True if the generator regulates voltage |
| $EnergySource$ | | The energy source harnessed to turn the generator |
| $IsCondenser$ | | True if the generator may behave as a condenser |

**Specifications**

Expand All @@ -180,6 +181,9 @@ The `VoltageRegulatorOn` attribute is required. It voltage regulation is enabled

Target values for generators (`TargetP` and `TargetQ`) follow the generator sign convention: a positive value means an injection into the bus. Positive values for `TargetP` and `TargetQ` mean negative values at the flow observed at the generator `Terminal`, as `Terminal` flow always follows load sign convention. The following diagram shows the sign convention of these quantities with an example.

The `isCondenser` value corresponds for instance to generators which can control voltage even if their targetP is equal to zero.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeandemanged or under Pmin?

Copy link
Member

@jeandemanged jeandemanged Jun 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in OLF we have another parameter to disable voltage control if outside Pmin or Pmax (globally, not specific to condensers).



**Available extensions**

- [Active Power Control](extensions.md#active-power-control)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,11 @@ public interface Generator extends Injection<Generator>, ReactiveLimitsHolder {

Generator setRatedS(double ratedS);

/**
* Get whether the generator may behave as a condenser, for instance if it may control voltage even if its targetP is equal to zero.
*/
boolean isCondenser();

@Override
default IdentifiableType getType() {
return IdentifiableType.GENERATOR;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public interface GeneratorAdder extends InjectionAdder<Generator, GeneratorAdder

GeneratorAdder setRatedS(double ratedS);

/**
* Set whether the generator may behave as a condenser, for instance if it may control voltage even if its targetP is equal to zero.
*/
GeneratorAdder setCondenser(boolean isCondenser);

/**
* Build the Generator object.
* <br>These are the checks that are performed before creating the object :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class GeneratorAdderImpl extends AbstractInjectionAdder<GeneratorAdderImpl> impl

private double ratedS = Double.NaN;

private boolean isCondenser = false;

GeneratorAdderImpl(VoltageLevelExt voltageLevel) {
this.voltageLevel = voltageLevel;
}
Expand Down Expand Up @@ -96,6 +98,12 @@ public GeneratorAdder setRatedS(double ratedS) {
return this;
}

@Override
public GeneratorAdder setCondenser(boolean isCondenser) {
this.isCondenser = isCondenser;
return this;
}

@Override
public GeneratorImpl add() {
NetworkImpl network = getNetwork();
Expand All @@ -121,7 +129,7 @@ id, getName(), isFictitious(), energySource,
minP, maxP,
voltageRegulatorOn, regulatingTerminal != null ? regulatingTerminal : terminal,
targetP, targetQ, targetV,
ratedS);
ratedS, isCondenser);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@geofjamg I think that we have to add a validity checks or not, need to think about it:

  • targetP must be zero if condenser = true.
  • or we consider that all active power related attributes must be ignored? For example, the generator should be excluded form active power distribution so it is natural to ignore related data.
    What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • targetP must be zero if condenser = true.

I'd be against that, or, for a practical use, it would require isCondenser to be renamed to isCondensing + make it variant dependent.

  • or we consider that all active power related attributes must be ignored? For example, the generator should be excluded form active power distribution so it is natural to ignore related data.
    What do you think?

My call would be that isCondenser should be used/checked only when targetP is zero.
Conversely, if targetP is not zero, then isCondenser should not be used/checked.

generator.addTerminal(terminal);
voltageLevel.attach(terminal, false);
network.getIndex().checkAndAdd(generator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
*/
package com.powsybl.iidm.network.impl;

import com.powsybl.iidm.network.*;
import com.powsybl.commons.ref.Ref;
import com.powsybl.iidm.network.*;
import gnu.trove.list.array.TDoubleArrayList;

/**
Expand Down Expand Up @@ -38,12 +38,14 @@ class GeneratorImpl extends AbstractConnectable<Generator> implements Generator,

private final TDoubleArrayList targetV;

private final boolean isCondenser;

GeneratorImpl(Ref<NetworkImpl> network,
String id, String name, boolean fictitious, EnergySource energySource,
double minP, double maxP,
boolean voltageRegulatorOn, TerminalExt regulatingTerminal,
double targetP, double targetQ, double targetV,
double ratedS) {
double ratedS, boolean isCondenser) {
super(network, id, name, fictitious);
this.network = network;
this.energySource = energySource;
Expand All @@ -62,6 +64,7 @@ class GeneratorImpl extends AbstractConnectable<Generator> implements Generator,
this.targetQ.add(targetQ);
this.targetV.add(targetV);
}
this.isCondenser = isCondenser;
}

@Override
Expand Down Expand Up @@ -214,6 +217,11 @@ public GeneratorImpl setRatedS(double ratedS) {
return this;
}

@Override
public boolean isCondenser() {
return isCondenser;
}

@Override
public ReactiveCapabilityCurveAdderImpl newReactiveCapabilityCurve() {
return new ReactiveCapabilityCurveAdderImpl(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package com.powsybl.iidm.serde;

import com.powsybl.iidm.network.*;
import com.powsybl.iidm.serde.util.IidmSerDeUtil;

import static com.powsybl.iidm.serde.ConnectableSerDeUtil.*;

Expand Down Expand Up @@ -36,6 +37,8 @@ protected void writeRootElementAttributes(Generator g, VoltageLevel vl, NetworkS
context.getWriter().writeDoubleAttribute("targetP", g.getTargetP());
context.getWriter().writeDoubleAttribute("targetV", g.getTargetV());
context.getWriter().writeDoubleAttribute("targetQ", g.getTargetQ());
IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_13, context, () ->
context.getWriter().writeBooleanAttribute("isCondenser", g.isCondenser(), false));
writeNodeOrBus(null, g.getTerminal(), context);
writePQ(null, g.getTerminal(), context.getWriter());
}
Expand Down Expand Up @@ -63,6 +66,8 @@ protected Generator readRootElementAttributes(GeneratorAdder adder, VoltageLevel
double targetP = context.getReader().readDoubleAttribute("targetP");
double targetV = context.getReader().readDoubleAttribute("targetV");
double targetQ = context.getReader().readDoubleAttribute("targetQ");
IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_13, context, () ->
adder.setCondenser(context.getReader().readBooleanAttribute("isCondenser", false)));
readNodeOrBus(adder, context, voltageLevel.getTopologyKind());
adder.setEnergySource(energySource)
.setMinP(minP)
Expand Down
1 change: 1 addition & 0 deletions iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@
<xs:attribute name="targetP" use="required" type="xs:double"/>
<xs:attribute name="targetQ" use="optional" type="xs:double"/>
<xs:attribute name="targetV" use="optional" type="xs:double"/>
<xs:attribute name="isCondenser" use="optional" type="xs:boolean"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@
<xs:attribute name="targetP" use="optional" type="xs:double"/>
<xs:attribute name="targetQ" use="optional" type="xs:double"/>
<xs:attribute name="targetV" use="optional" type="xs:double"/>
<xs:attribute name="isCondenser" use="optional" type="xs:boolean"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ protected void allFormatsRoundTripFromVersionedXmlFromMinToCurrentVersionTest(St
.toArray(IidmVersion[]::new));
}

/**
* Execute a round trip test on the test resource IIDM-XML file with a given file name for all IIDM versions
* equals or more recent than a given minimum IIDM version.
*/
protected void allFormatsRoundTripFromVersionedXmlFromMinVersionTest(String file, IidmVersion minVersion) throws IOException {
jeandemanged marked this conversation as resolved.
Show resolved Hide resolved
allFormatsRoundTripFromVersionedXmlTest(file, Stream.of(IidmVersion.values())
.filter(v -> v.compareTo(minVersion) >= 0)
.toArray(IidmVersion[]::new));
}

/**
* Execute a round trip test on the test resource IIDM-JSON file with a given file name for all IIDM versions
* equals or more recent than a given minimum IIDM version <b>and</b> strictly older than the current IIDM version.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copyright (c) 2024, 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.iidm.serde;

import org.junit.jupiter.api.Test;

import java.io.IOException;

/**
* @author Florian Dupuy {@literal <florian.dupuy at rte-france.com>}
*/
class GeneratorSerDeTest extends AbstractIidmSerDeTest {

@Test
void isCondenserTest() throws IOException {
allFormatsRoundTripFromVersionedXmlFromMinVersionTest("generator.xml", IidmVersion.V_1_12);
}
}
20 changes: 20 additions & 0 deletions iidm/iidm-serde/src/test/resources/V1_12/generator.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_12" id="internal-connections" caseDate="2018-11-08T12:33:26.208+01:00" forecastDistance="0" sourceFormat="test" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
<iidm:substation id="substation" country="FI">
<iidm:voltageLevel id="voltageLevel" nominalV="400.0" topologyKind="NODE_BREAKER">
<iidm:nodeBreakerTopology>
<iidm:busbarSection id="bbs" node="3"/>
<iidm:switch id="breaker1" kind="BREAKER" retained="false" open="false" node1="0" node2="3"/>
<iidm:switch id="breaker2" kind="BREAKER" retained="false" open="false" node1="1" node2="3"/>
<iidm:switch id="breaker3" kind="BREAKER" retained="false" open="false" node1="2" node2="3"/>
</iidm:nodeBreakerTopology>
<iidm:generator id="generator1" energySource="OTHER" minP="0.0" maxP="100.0" voltageRegulatorOn="true" targetP="10.0" targetV="400.0" node="0">
<iidm:minMaxReactiveLimits minQ="-1.7976931348623157E308" maxQ="1.7976931348623157E308"/>
</iidm:generator>
<iidm:generator id="generator2" energySource="OTHER" minP="0.0" maxP="100.0" voltageRegulatorOn="true" targetP="10.0" targetV="400.0" node="1">
<iidm:minMaxReactiveLimits minQ="-1.7976931348623157E308" maxQ="1.7976931348623157E308"/>
</iidm:generator>
<iidm:load id="load" loadType="UNDEFINED" p0="10.0" q0="1.0" node="2"/>
</iidm:voltageLevel>
</iidm:substation>
</iidm:network>
20 changes: 20 additions & 0 deletions iidm/iidm-serde/src/test/resources/V1_13/generator.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_13" id="internal-connections" caseDate="2018-11-08T12:33:26.208+01:00" forecastDistance="0" sourceFormat="test" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
<iidm:substation id="substation" country="FI">
<iidm:voltageLevel id="voltageLevel" nominalV="400.0" topologyKind="NODE_BREAKER">
<iidm:nodeBreakerTopology>
<iidm:busbarSection id="bbs" node="3"/>
<iidm:switch id="breaker1" kind="BREAKER" retained="false" open="false" node1="0" node2="3"/>
<iidm:switch id="breaker2" kind="BREAKER" retained="false" open="false" node1="1" node2="3"/>
<iidm:switch id="breaker3" kind="BREAKER" retained="false" open="false" node1="2" node2="3"/>
</iidm:nodeBreakerTopology>
<iidm:generator id="generator1" energySource="OTHER" minP="0.0" maxP="100.0" voltageRegulatorOn="true" targetP="10.0" targetV="400.0" isCondenser="true" node="0">
<iidm:minMaxReactiveLimits minQ="-1.7976931348623157E308" maxQ="1.7976931348623157E308"/>
</iidm:generator>
<iidm:generator id="generator2" energySource="OTHER" minP="0.0" maxP="100.0" voltageRegulatorOn="true" targetP="10.0" targetV="400.0" node="1">
<iidm:minMaxReactiveLimits minQ="-1.7976931348623157E308" maxQ="1.7976931348623157E308"/>
</iidm:generator>
<iidm:load id="load" loadType="UNDEFINED" p0="10.0" q0="1.0" node="2"/>
</iidm:voltageLevel>
</iidm:substation>
</iidm:network>
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,20 @@ public void testSetterGetter() {
assertFalse(generator.isVoltageRegulatorOn());
generator.setVoltageRegulatorOn(true);
assertTrue(generator.isVoltageRegulatorOn());
assertFalse(generator.isCondenser());

assertEquals(12, generator.getTerminal().getNodeBreakerView().getNode());
}

@Test
public void undefinedVoltageRegulatorOn() {
ValidationException e = assertThrows(ValidationException.class, () -> voltageLevel.newGenerator()
GeneratorAdder generatorAdder = voltageLevel.newGenerator()
.setId("GEN")
.setMaxP(Double.MAX_VALUE)
.setMinP(-Double.MAX_VALUE)
.setTargetP(30.0)
.setNode(1)
.add());
.setNode(1);
ValidationException e = assertThrows(ValidationException.class, generatorAdder::add);
assertEquals("Generator 'GEN': voltage regulator status is not set", e.getMessage());
}

Expand Down Expand Up @@ -191,6 +192,7 @@ public void testAdder() {
.setTargetQ(20.0)
.setNode(1)
.setTargetV(31.0)
.setCondenser(true)
.add();
Generator generator = network.getGenerator(GEN_ID);
assertNotNull(generator);
Expand All @@ -203,6 +205,7 @@ public void testAdder() {
assertEquals(30.0, generator.getTargetP(), 0.0);
assertEquals(20.0, generator.getTargetQ(), 0.0);
assertEquals(31.0, generator.getTargetV(), 0.0);
assertTrue(generator.isCondenser());
}

@Test
Expand Down