Skip to content

Commit

Permalink
ActivePowerControl extension: Rename optional fields min/maxPOverri…
Browse files Browse the repository at this point in the history
…de -> min/maxTargetP (#3084)

* rename optional fields min/maxPOverride -> min/maxTargetP
* review feedback

Signed-off-by: Didier Vidal <didier.vidal_externe@rte-france.com>
(cherry picked from commit 1a81b7e)
  • Loading branch information
vidaldid-rte authored and olperr1 committed Jul 1, 2024
1 parent aad8e0a commit aacd7b4
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 77 deletions.
8 changes: 4 additions & 4 deletions docs/grid_model/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ This extension is used to configure the participation factor of the generator, t
| participate | boolean | - | yes | - | The participation status |
| droop | double | None (repartition key) | no | - | The participation factor equals maxP / droop |
| participation factor | double | None (repartition key) | no | - | Defines the participation factor explicitly |
| maxP override | double | MW | no | - | If defined, this limit is used for slack distribution instead of the generator's maxP |
| minP override | double | MW | no | - | if defined, this limit is used for slack distribution instead of the generator's minP |
| max targetP | double | MW | no | - | If defined, this limit is used for slack distribution instead of the generator's maxP |
| min targetP | double | MW | no | - | if defined, this limit is used for slack distribution instead of the generator's minP |

Here is how to add an active power control extension to a generator:
```java
Expand All @@ -31,8 +31,8 @@ generator.newExtension(ActivePowerControlAdder.class)
.withParticipationFactor(1.5)
.add();
```

The participation status, the participation factor, the maxP override and the minP override are multi-variants: they can vary from one variant to another.
If defined, min targetP and max targetP must be in the [pMin, pMax] interval of the Generator or Battery.
The participation status, the participation factor, the max targetP and the min targetP are multi-variants: they can vary from one variant to another.

This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,24 @@ default String getName() {
/**
* @return if present, provides the overridden value of minP to be used for active power control operations.
*/
OptionalDouble getMinPOverride();
OptionalDouble getMinTargetP();

/**
* Sets the overridden minimal active power.
* @param pMinOverride The overridden value of minP. A Nan value removes the override.
* Sets the minimum value for targetP. The value must be in the [pmin,pmax] interval of the extended
* generator or battery.
* @param minTargetP The overridden value of minP. A Nan value removes the override.
*/
void setMinPOverride(double pMinOverride);
void setMinTargetP(double minTargetP);

/**
* @return if present, provides the overridden value of maxP to be used for active power control operations.
*/
OptionalDouble getMaxPOverride();
OptionalDouble getMaxTargetP();

/**
* Sets the overridden maximal active power.
* @param pMaxOverride The overridden value of maxP. A Nan value removes the override.
* Sets the maximum value for targetP. The value must be in the [pmin,pmax] interval of the extended
* generator or battery.
* @param maxTargetP The overridden value of maxP. A Nan value removes the override.
*/
void setMaxPOverride(double pMaxOverride);
void setMaxTargetP(double maxTargetP);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ default Class<ActivePowerControl> getExtensionClass() {

ActivePowerControlAdder<I> withParticipationFactor(double participationFactor);

ActivePowerControlAdder<I> withMinPOverride(double minPOverride);
ActivePowerControlAdder<I> withMinTargetP(double minTargetP);

ActivePowerControlAdder<I> withMaxPOverride(double maxPOverride);
ActivePowerControlAdder<I> withMaxTargetP(double maxTargetP);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ public class ActivePowerControlAdderImpl<I extends Injection<I>>

private double droop = Double.NaN;
private double participationFactor = Double.NaN;
private double minPOverride = Double.NaN;
private double maxPOverride = Double.NaN;
private double minTargetP = Double.NaN;
private double maxTargetP = Double.NaN;

protected ActivePowerControlAdderImpl(I extendable) {
super(extendable);
}

@Override
protected ActivePowerControlImpl<I> createExtension(I extendable) {
return new ActivePowerControlImpl<>(extendable, participate, droop, participationFactor, minPOverride, maxPOverride);
return new ActivePowerControlImpl<>(extendable, participate, droop, participationFactor, minTargetP, maxTargetP);
}

@Override
Expand All @@ -51,14 +51,14 @@ public ActivePowerControlAdder<I> withParticipationFactor(double participationFa
}

@Override
public ActivePowerControlAdder<I> withMinPOverride(double minPOverride) {
this.minPOverride = minPOverride;
public ActivePowerControlAdder<I> withMinTargetP(double minTargetP) {
this.minTargetP = minTargetP;
return this;
}

@Override
public ActivePowerControlAdder<I> withMaxPOverride(double maxPOverride) {
this.maxPOverride = maxPOverride;
public ActivePowerControlAdder<I> withMaxTargetP(double maxTargetP) {
this.maxTargetP = maxTargetP;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
*/
package com.powsybl.iidm.network.impl.extensions;

import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.util.trove.TBooleanArrayList;
import com.powsybl.iidm.network.Battery;
import com.powsybl.iidm.network.Generator;
import com.powsybl.iidm.network.Injection;
import com.powsybl.iidm.network.extensions.ActivePowerControl;
import com.powsybl.iidm.network.impl.AbstractMultiVariantIdentifiableExtension;
import gnu.trove.list.array.TDoubleArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.OptionalDouble;
Expand All @@ -22,13 +27,14 @@
public class ActivePowerControlImpl<T extends Injection<T>> extends AbstractMultiVariantIdentifiableExtension<T>
implements ActivePowerControl<T> {

private static final Logger LOGGER = LoggerFactory.getLogger(ActivePowerControlImpl.class);
private final TBooleanArrayList participate;

private final TDoubleArrayList droop;
private final TDoubleArrayList participationFactor;

private final TDoubleArrayList minPOverride;
private final TDoubleArrayList maxPOverride;
private final TDoubleArrayList minTargetP;
private final TDoubleArrayList maxTargetP;

private final List<TDoubleArrayList> allTDoubleArrayLists;

Expand All @@ -43,22 +49,71 @@ public ActivePowerControlImpl(T component,
boolean participate,
double droop,
double participationFactor,
double minPOverride,
double maxPOverride) {
double minTargetP,
double maxTargetP) {
super(component);
int variantArraySize = getVariantManagerHolder().getVariantManager().getVariantArraySize();
this.participate = new TBooleanArrayList(variantArraySize);
this.droop = new TDoubleArrayList(variantArraySize);
this.participationFactor = new TDoubleArrayList(variantArraySize);
this.minPOverride = new TDoubleArrayList(variantArraySize);
this.maxPOverride = new TDoubleArrayList(variantArraySize);
this.allTDoubleArrayLists = List.of(this.droop, this.participationFactor, this.minPOverride, this.maxPOverride);
this.minTargetP = new TDoubleArrayList(variantArraySize);
this.maxTargetP = new TDoubleArrayList(variantArraySize);
this.allTDoubleArrayLists = List.of(this.droop, this.participationFactor, this.minTargetP, this.maxTargetP);
double checkedMinTargetP = checkTargetPLimit(minTargetP, "minTargetP", component);
double checkedMaxTargetP = checkTargetPLimit(maxTargetP, "maxTargetP", component);
for (int i = 0; i < variantArraySize; i++) {
this.participate.add(participate);
this.droop.add(droop);
this.participationFactor.add(participationFactor);
this.minPOverride.add(minPOverride);
this.maxPOverride.add(maxPOverride);
this.minTargetP.add(checkedMinTargetP);
this.maxTargetP.add(checkedMaxTargetP);
}
checkLimitOrder(minTargetP, maxTargetP);
}

record PLimits(double minP, double maxP) { }

private PLimits getPLimits(T injection) {
double maxP = Double.MAX_VALUE;
double minP = -Double.MAX_VALUE;
if (injection instanceof Generator generator) {
maxP = generator.getMaxP();
minP = generator.getMinP();
} else if (injection instanceof Battery battery) {
maxP = battery.getMaxP();
minP = battery.getMinP();
}
return new PLimits(minP, maxP);
}

private double withinPMinMax(double value, T injection) {
PLimits pLimits = getPLimits(injection);

if (!Double.isNaN(value) && (value < pLimits.minP || value > pLimits.maxP)) {
LOGGER.warn("targetP limit is now outside of pMin,pMax for component {}. Returning closest value in [pmin,pMax].",
injection.getId());
return value < pLimits.minP ? pLimits.minP : pLimits.maxP;
}
return value;
}

private double checkTargetPLimit(double targetPLimit, String name, T injection) {
PLimits pLimits = getPLimits(injection);

if (!Double.isNaN(targetPLimit) && (targetPLimit < pLimits.minP || targetPLimit > pLimits.maxP)) {
throw new PowsyblException(String.format("%s value (%s) is not between minP and maxP for component %s",
name,
targetPLimit,
injection.getId()));
}

return targetPLimit;
}

private void checkLimitOrder(double minTargetP, double maxTargetP) {
if (!Double.isNaN(minTargetP) && !Double.isNaN(maxTargetP)
&& minTargetP > maxTargetP) {
throw new PowsyblException("invalid targetP limits [" + minTargetP + ", " + maxTargetP + "]");
}
}

Expand Down Expand Up @@ -116,24 +171,26 @@ public void allocateVariantArrayElement(int[] indexes, int sourceIndex) {
}

@Override
public OptionalDouble getMinPOverride() {
double result = minPOverride.get(getVariantIndex());
return Double.isNaN(result) ? OptionalDouble.empty() : OptionalDouble.of(result);
public OptionalDouble getMinTargetP() {
double result = minTargetP.get(getVariantIndex());
return Double.isNaN(result) ? OptionalDouble.empty() : OptionalDouble.of(withinPMinMax(result, getExtendable()));
}

@Override
public void setMinPOverride(double minPOverride) {
this.minPOverride.set(getVariantIndex(), minPOverride);
public void setMinTargetP(double minTargetP) {
checkLimitOrder(minTargetP, maxTargetP.get(getVariantIndex()));
this.minTargetP.set(getVariantIndex(), checkTargetPLimit(minTargetP, "minTargetP", getExtendable()));
}

@Override
public OptionalDouble getMaxPOverride() {
double result = maxPOverride.get(getVariantIndex());
return Double.isNaN(result) ? OptionalDouble.empty() : OptionalDouble.of(result);
public OptionalDouble getMaxTargetP() {
double result = maxTargetP.get(getVariantIndex());
return Double.isNaN(result) ? OptionalDouble.empty() : OptionalDouble.of(withinPMinMax(result, getExtendable()));
}

@Override
public void setMaxPOverride(double maxPOverride) {
this.maxPOverride.set(getVariantIndex(), maxPOverride);
public void setMaxTargetP(double maxTargetP) {
checkLimitOrder(minTargetP.get(getVariantIndex()), maxTargetP);
this.maxTargetP.set(getVariantIndex(), checkTargetPLimit(maxTargetP, "maxTargetP", getExtendable()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ public void write(ActivePowerControl<T> activePowerControl, SerializerContext co
}
if ("1.2".compareTo(extVersionStr) <= 0) {
// not using writeOptionalDouble and trusting implementation convention: : writeDoubleAttribute does not write NaN values in human-readable formats JSON/XML
context.getWriter().writeDoubleAttribute("maxPOverride", activePowerControl.getMaxPOverride().orElse(Double.NaN));
context.getWriter().writeDoubleAttribute("minPOverride", activePowerControl.getMinPOverride().orElse(Double.NaN));
context.getWriter().writeDoubleAttribute("maxTargetP", activePowerControl.getMaxTargetP().orElse(Double.NaN));
context.getWriter().writeDoubleAttribute("minTargetP", activePowerControl.getMinTargetP().orElse(Double.NaN));
}
}

Expand All @@ -89,25 +89,25 @@ public ActivePowerControl<T> read(T identifiable, DeserializerContext context) {
boolean participate = context.getReader().readBooleanAttribute("participate");
double droop = context.getReader().readDoubleAttribute("droop");
double participationFactor = Double.NaN;
double minPOverride = Double.NaN;
double maxPOverride = Double.NaN;
double minTargetP = Double.NaN;
double maxTargetP = Double.NaN;
NetworkDeserializerContext networkContext = (NetworkDeserializerContext) context;
String extVersionStr = networkContext.getExtensionVersion(this).orElseThrow(IllegalStateException::new);
if ("1.1".compareTo(extVersionStr) <= 0) {
participationFactor = context.getReader().readDoubleAttribute("participationFactor");
}
if ("1.2".compareTo(extVersionStr) <= 0) {
// not using readOptionalDouble and trusting implementation convention: readDoubleAttribute returns Nan if attribute is absent in human-readable formats (JSON / XML)
maxPOverride = context.getReader().readDoubleAttribute("maxPOverride");
minPOverride = context.getReader().readDoubleAttribute("minPOverride");
maxTargetP = context.getReader().readDoubleAttribute("maxTargetP");
minTargetP = context.getReader().readDoubleAttribute("minTargetP");
}
context.getReader().readEndNode();
ActivePowerControlAdder<T> activePowerControlAdder = identifiable.newExtension(ActivePowerControlAdder.class);
return activePowerControlAdder.withParticipate(participate)
.withDroop(droop)
.withParticipationFactor(participationFactor)
.withMinPOverride(minPOverride)
.withMaxPOverride(maxPOverride)
.withMinTargetP(minTargetP)
.withMaxTargetP(maxTargetP)
.add();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
<xs:attribute name="participate" use="required" type="xs:boolean"/>
<xs:attribute name="droop" use="optional" type="xs:double"/>
<xs:attribute name="participationFactor" use="optional" type="xs:double"/>
<xs:attribute name="minPOverride" use="optional" type="xs:double"/>
<xs:attribute name="maxPOverride" use="optional" type="xs:double"/>
<xs:attribute name="minTargetP" use="optional" type="xs:double"/>
<xs:attribute name="maxTargetP" use="optional" type="xs:double"/>
</xs:complexType>
</xs:element>
</xs:schema>
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class ActivePowerControlXmlTest extends AbstractIidmSerDeTest {

private Network network;

@Override
@BeforeEach
public void setUp() throws IOException {
super.setUp();
Expand All @@ -47,24 +48,24 @@ public void setUp() throws IOException {
}

@Test
void testPLimitOverride() throws IOException {
network.getGenerator("GEN").getExtension(ActivePowerControl.class).setMaxPOverride(100.);
network.getBattery("BAT").getExtension(ActivePowerControl.class).setMinPOverride(10.);
void testTargetPLimits() throws IOException {
network.getGenerator("GEN").getExtension(ActivePowerControl.class).setMaxTargetP(800.);
network.getBattery("BAT").getExtension(ActivePowerControl.class).setMinTargetP(10.);
Network network2 = allFormatsRoundTripTest(network, "/activePowerControlWithLimitRoundTripRef.xml", CURRENT_IIDM_VERSION);

Generator gen2 = network2.getGenerator("GEN");
assertNotNull(gen2);
ActivePowerControl<Generator> activePowerControl1 = gen2.getExtension(ActivePowerControl.class);
assertNotNull(activePowerControl1);
assertEquals(OptionalDouble.of(100), activePowerControl1.getMaxPOverride());
assertTrue(activePowerControl1.getMinPOverride().isEmpty());
assertEquals(OptionalDouble.of(800), activePowerControl1.getMaxTargetP());
assertTrue(activePowerControl1.getMinTargetP().isEmpty());

Battery bat2 = network2.getBattery("BAT");
assertNotNull(bat2);
ActivePowerControl<Battery> activePowerControl2 = bat2.getExtension(ActivePowerControl.class);
assertNotNull(activePowerControl2);
assertTrue(activePowerControl2.getMaxPOverride().isEmpty());
assertEquals(OptionalDouble.of(10), activePowerControl2.getMinPOverride());
assertTrue(activePowerControl2.getMaxTargetP().isEmpty());
assertEquals(OptionalDouble.of(10), activePowerControl2.getMinTargetP());
}

@Test
Expand All @@ -75,8 +76,8 @@ void testIidmV12() throws IOException {
assertNotNull(bat2);
ActivePowerControl<Battery> activePowerControl2 = bat2.getExtension(ActivePowerControl.class);
assertNotNull(activePowerControl2);
assertTrue(activePowerControl2.getMaxPOverride().isEmpty());
assertTrue(activePowerControl2.getMinPOverride().isEmpty());
assertTrue(activePowerControl2.getMaxTargetP().isEmpty());
assertTrue(activePowerControl2.getMinTargetP().isEmpty());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
<iidm:line id="NHV1_NHV2_1" r="3.0" x="33.0" g1="0.0" b1="1.93E-4" g2="0.0" b2="1.93E-4" bus1="NGEN" connectableBus1="NGEN" voltageLevelId1="VLGEN" bus2="NBAT" connectableBus2="NBAT" voltageLevelId2="VLBAT"/>
<iidm:line id="NHV1_NHV2_2" r="3.0" x="33.0" g1="0.0" b1="1.93E-4" g2="0.0" b2="1.93E-4" bus1="NGEN" connectableBus1="NGEN" voltageLevelId1="VLGEN" bus2="NBAT" connectableBus2="NBAT" voltageLevelId2="VLBAT"/>
<iidm:extension id="GEN">
<apc:activePowerControl participate="false" droop="3.0" participationFactor="1.0" maxPOverride="100.0"/>
<apc:activePowerControl participate="false" droop="3.0" participationFactor="1.0" maxTargetP="800.0"/>
</iidm:extension>
<iidm:extension id="BAT">
<apc:activePowerControl participate="true" droop="4.0" participationFactor="1.2" minPOverride="10.0"/>
<apc:activePowerControl participate="true" droop="4.0" participationFactor="1.2" minTargetP="10.0"/>
</iidm:extension>
</iidm:network>
Loading

0 comments on commit aacd7b4

Please sign in to comment.