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

Active Power Control extension: add participationFactor #2402

Merged
merged 8 commits into from
Dec 13, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,32 @@ default String getName() {

void setParticipate(boolean participate);

float getDroop();

void setDroop(float droop);
/**
* This is the change in generator power output divided by the change in frequency
* normalized by the nominal power of the generator and the nominal frequency and
* expressed in percent and negated. A positive value of speed change droop provides
* additional generator output upon a drop in frequency.
* @return Governor Speed Changer Droop.
*/
double getDroop();

/**
* @param droop new Governor Speed Changer Droop value
*/
void setDroop(double droop);

/**
* Generating unit participation factor.
* The sum of the participation factors across generating units does not have to sum to one.
* It is used for representing distributed slack participation factor.
* The attribute shall be a positive value or zero.
* @return Generating unit participation factor.
*/
double getParticipationFactor();

/**
* @param participationFactor new Generating unit participation factor value
*/
void setParticipationFactor(double participationFactor);

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ default Class<ActivePowerControl> getExtensionClass() {

ActivePowerControlAdder<I> withParticipate(boolean participate);

ActivePowerControlAdder<I> withDroop(float droop);
ActivePowerControlAdder<I> withDroop(double droop);

ActivePowerControlAdder<I> withParticipationFactor(double participationFactor);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<xs:complexType>
<xs:attribute name="participate" use="required" type="xs:boolean"/>
<xs:attribute name="droop" use="required" type="xs:double"/>
<xs:attribute name="participationFactor" type="xs:double"/>
</xs:complexType>
</xs:element>
</xs:schema>
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ public class ActivePowerControlAdderImpl<I extends Injection<I>>

private boolean participate;

private float droop;
private double droop;
private double participationFactor;

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

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

@Override
Expand All @@ -35,9 +36,15 @@ public ActivePowerControlAdder<I> withParticipate(boolean participate) {
}

@Override
public ActivePowerControlAdder<I> withDroop(float droop) {
public ActivePowerControlAdder<I> withDroop(double droop) {
this.droop = droop;
return this;
}

@Override
public ActivePowerControlAdder<I> withParticipationFactor(double participationFactor) {
this.participationFactor = participationFactor;
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,37 @@
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.TFloatArrayList;
import gnu.trove.list.array.TDoubleArrayList;

import java.util.List;

/**
* @author Ghiles Abdellah <ghiles.abdellah at rte-france.com>
*/
public class ActivePowerControlImpl<T extends Injection<T>> extends AbstractMultiVariantIdentifiableExtension<T>
implements ActivePowerControl<T> {

private TBooleanArrayList participate;
private final TBooleanArrayList participate;

private final TDoubleArrayList droop;
private final TDoubleArrayList participationFactor;

private TFloatArrayList droop;
private final List<TDoubleArrayList> allTDoubleArrayLists;

public ActivePowerControlImpl(T component, boolean participate, float droop) {
public ActivePowerControlImpl(T component,
boolean participate,
double droop,
double participationFactor) {
super(component);
int variantArraySize = getVariantManagerHolder().getVariantManager().getVariantArraySize();
this.participate = new TBooleanArrayList(variantArraySize);
this.droop = new TFloatArrayList(variantArraySize);
this.droop = new TDoubleArrayList(variantArraySize);
this.participationFactor = new TDoubleArrayList(variantArraySize);
this.allTDoubleArrayLists = List.of(this.droop, this.participationFactor);
for (int i = 0; i < variantArraySize; i++) {
this.participate.add(participate);
this.droop.add(droop);
this.participationFactor.add(participationFactor);
}
}

Expand All @@ -41,28 +52,36 @@ public void setParticipate(boolean participate) {
this.participate.set(getVariantIndex(), participate);
}

public float getDroop() {
public double getDroop() {
return droop.get(getVariantIndex());
}

public void setDroop(float droop) {
public void setDroop(double droop) {
this.droop.set(getVariantIndex(), droop);
}

public double getParticipationFactor() {
return participationFactor.get(getVariantIndex());
}

public void setParticipationFactor(double participationFactor) {
this.participationFactor.set(getVariantIndex(), participationFactor);
}

@Override
public void extendVariantArraySize(int initVariantArraySize, int number, int sourceIndex) {
participate.ensureCapacity(participate.size() + number);
droop.ensureCapacity(droop.size() + number);
allTDoubleArrayLists.forEach(dl -> dl.ensureCapacity(dl.size() + number));
for (int i = 0; i < number; ++i) {
participate.add(participate.get(sourceIndex));
droop.add(droop.get(sourceIndex));
allTDoubleArrayLists.forEach(dl -> dl.add(dl.get(sourceIndex)));
}
}

@Override
public void reduceVariantArraySize(int number) {
participate.remove(participate.size() - number, number);
droop.remove(droop.size() - number, number);
allTDoubleArrayLists.forEach(dl -> dl.remove(dl.size() - number, number));
}

@Override
Expand All @@ -75,6 +94,7 @@ public void allocateVariantArrayElement(int[] indexes, int sourceIndex) {
for (int index : indexes) {
participate.set(index, participate.get(sourceIndex));
droop.set(index, droop.get(sourceIndex));
allTDoubleArrayLists.forEach(dl -> dl.set(index, dl.get(sourceIndex)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,19 @@ public void test() {
Battery bat = network.getBattery("BAT");
assertNotNull(bat);
bat.newExtension(ActivePowerControlAdder.class)
.withDroop(4f)
.withDroop(4.0)
.withParticipate(true)
.withParticipationFactor(1.2)
.add();
ActivePowerControl<Battery> activePowerControl = bat.getExtension(ActivePowerControl.class);
assertEquals("activePowerControl", activePowerControl.getName());
assertEquals("BAT", activePowerControl.getExtendable().getId());

assertTrue(activePowerControl.isParticipate());
assertEquals(4f, activePowerControl.getDroop(), 0f);
checkValues1(activePowerControl);
activePowerControl.setParticipate(false);
assertFalse(activePowerControl.isParticipate());
activePowerControl.setDroop(6f);
assertEquals(6f, activePowerControl.getDroop(), 0f);
activePowerControl.setDroop(6.0);
activePowerControl.setParticipationFactor(3.0);
checkValues2(activePowerControl);
}

@Test
Expand All @@ -58,8 +58,9 @@ public void variantsCloneTest() {
Battery bat = network.getBattery("BAT");
assertNotNull(bat);
bat.newExtension(ActivePowerControlAdder.class)
.withDroop(4f)
.withDroop(4.0)
.withParticipate(true)
.withParticipationFactor(1.2)
.add();
ActivePowerControl<Battery> activePowerControl = bat.getExtension(ActivePowerControl.class);
assertNotNull(activePowerControl);
Expand All @@ -69,28 +70,24 @@ public void variantsCloneTest() {
variantManager.cloneVariant(INITIAL_VARIANT_ID, variant1);
variantManager.cloneVariant(variant1, variant2);
variantManager.setWorkingVariant(variant1);
assertTrue(activePowerControl.isParticipate());
assertEquals(4f, activePowerControl.getDroop(), 0f);
checkValues1(activePowerControl);

// Testing setting different values in the cloned variant and going back to the initial one
activePowerControl.setDroop(5f);
activePowerControl.setDroop(6.0);
activePowerControl.setParticipate(false);
assertFalse(activePowerControl.isParticipate());
assertEquals(5f, activePowerControl.getDroop(), 0f);
activePowerControl.setParticipationFactor(3.0);
checkValues2(activePowerControl);
variantManager.setWorkingVariant(INITIAL_VARIANT_ID);
assertTrue(activePowerControl.isParticipate());
assertEquals(4f, activePowerControl.getDroop(), 0f);
checkValues1(activePowerControl);

// Removes a variant then adds another variant to test variant recycling (hence calling allocateVariantArrayElement)
variantManager.removeVariant(variant1);
List<String> targetVariantIds = Arrays.asList(variant1, variant3);
variantManager.cloneVariant(INITIAL_VARIANT_ID, targetVariantIds);
variantManager.setWorkingVariant(variant1);
assertTrue(activePowerControl.isParticipate());
assertEquals(4f, activePowerControl.getDroop(), 0f);
checkValues1(activePowerControl);
variantManager.setWorkingVariant(variant3);
assertTrue(activePowerControl.isParticipate());
assertEquals(4f, activePowerControl.getDroop(), 0f);
checkValues1(activePowerControl);

// Test removing current variant
variantManager.removeVariant(variant3);
Expand All @@ -101,4 +98,16 @@ public void variantsCloneTest() {
assertEquals("Variant index not set", e.getMessage());
}
}

private static void checkValues1(ActivePowerControl<Battery> activePowerControl) {
assertTrue(activePowerControl.isParticipate());
assertEquals(4.0, activePowerControl.getDroop(), 0.0);
assertEquals(1.2, activePowerControl.getParticipationFactor(), 0.0);
}

private static void checkValues2(ActivePowerControl<Battery> activePowerControl) {
assertFalse(activePowerControl.isParticipate());
assertEquals(6.0, activePowerControl.getDroop(), 0.0);
assertEquals(3.0, activePowerControl.getParticipationFactor(), 0.0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,19 @@ public ActivePowerControlXmlSerializer() {
@Override
public void write(ActivePowerControl activePowerControl, XmlWriterContext context) throws XMLStreamException {
context.getWriter().writeAttribute("participate", Boolean.toString(activePowerControl.isParticipate()));
XmlUtil.writeFloat("droop", activePowerControl.getDroop(), context.getWriter());
XmlUtil.writeDouble("droop", activePowerControl.getDroop(), context.getWriter());
XmlUtil.writeDouble("participationFactor", activePowerControl.getParticipationFactor(), context.getWriter());
}

@Override
public ActivePowerControl<T> read(T identifiable, XmlReaderContext context) {
boolean participate = XmlUtil.readBoolAttribute(context.getReader(), "participate");
float droop = XmlUtil.readFloatAttribute(context.getReader(), "droop");
double droop = XmlUtil.readDoubleAttribute(context.getReader(), "droop");
double participationFactor = XmlUtil.readOptionalDoubleAttribute(context.getReader(), "participationFactor", 0.0);
ActivePowerControlAdder<T> activePowerControlAdder = identifiable.newExtension(ActivePowerControlAdder.class);
return activePowerControlAdder.withParticipate(participate)
.withDroop(droop)
.withParticipationFactor(participationFactor)
.add();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ public void test() throws IOException {
Battery bat = network.getBattery("BAT");
assertNotNull(bat);

ActivePowerControl<Battery> activePowerControl = new ActivePowerControlImpl<>(bat, true, 4f);
ActivePowerControl<Battery> activePowerControl = new ActivePowerControlImpl<>(bat, true, 4.0, 1.2);
bat.addExtension(ActivePowerControl.class, activePowerControl);

Generator generator = network.getGenerator("GEN");
generator.addExtension(ActivePowerControl.class, new ActivePowerControlImpl<>(generator, false, 3));
generator.addExtension(ActivePowerControl.class, new ActivePowerControlImpl<>(generator, false, 3.0, 1.0));

Network network2 = roundTripXmlTest(network,
NetworkXml::writeAndValidate,
Expand All @@ -51,7 +51,8 @@ public void test() throws IOException {
assertNotNull(activePowerControl2);

assertEquals(activePowerControl.isParticipate(), activePowerControl2.isParticipate());
assertEquals(activePowerControl.getDroop(), activePowerControl2.getDroop(), 0f);
assertEquals(activePowerControl.getDroop(), activePowerControl2.getDroop(), 0.0);
assertEquals(activePowerControl.getParticipationFactor(), activePowerControl2.getParticipationFactor(), 0.0);
assertEquals(activePowerControl.getName(), activePowerControl2.getName());
}
}
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"/>
<apc:activePowerControl participate="false" droop="3.0" participationFactor="1.0"/>
</iidm:extension>
<iidm:extension id="BAT">
<apc:activePowerControl participate="true" droop="4.0"/>
<apc:activePowerControl participate="true" droop="4.0" participationFactor="1.2"/>
</iidm:extension>
</iidm:network>
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,33 @@ public enum VoltageInitMode {
DC_VALUES // preprocessing to compute DC angles
}

/**
* BalanceType enum describes the various options for active power slack distribution
*/
public enum BalanceType {
/**
* active power slack distribution on generators, proportional to generator targetP
*/
PROPORTIONAL_TO_GENERATION_P,
/**
* active power slack distribution on generators, proportional to generator maxP
*/
PROPORTIONAL_TO_GENERATION_P_MAX,
/**
* active power slack distribution on generators, proportional to generator maxP - targetP
*/
PROPORTIONAL_TO_GENERATION_REMAINING_MARGIN,
Copy link
Member

Choose a reason for hiding this comment

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

Remaining margin means proportional to pmax - targetP ?

Copy link
Member Author

@jeandemanged jeandemanged Dec 12, 2022

Choose a reason for hiding this comment

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

yes exactly (provided that pmax - targetP is positive)

could probably better renamed PROPORTIONAL_TO_GENERATION_RESERVE_MARGIN, or just PROPORTIONAL_TO_GENERATION_RESERVE ... any thought ?

Copy link
Member

Choose a reason for hiding this comment

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

PROPORTIONAL_TO_GENERATION_REMAINING_MARGIN seems ok to me

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should you add a comment to describe exactly what does it mean

Copy link
Member Author

Choose a reason for hiding this comment

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

I added javadoc to the options

/**
* active power slack distribution on generators, proportional to participationFactor (see ActivePowerControl extension)
*/
PROPORTIONAL_TO_GENERATION_PARTICIPATION_FACTOR,
/**
* active power slack distribution on all loads
*/
PROPORTIONAL_TO_LOAD,
/**
* active power slack distribution on conforming loads (see LoadDetails extension)
*/
PROPORTIONAL_TO_CONFORM_LOAD,
}

Expand Down