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] Extrapolation of getMinQ and getMaxQ for reactive capability curve #3250

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,15 @@ public interface Point {
*/
double getMaxP();

/**
* Get the reactive power minimum value of the curve (with the possibility of extrapolating slope of reactive
* limits outside active limits)
*/
double getMinQ(double p, boolean extrapolateReactiveLimitSlope);

SylvestreSakti marked this conversation as resolved.
Show resolved Hide resolved
/**
* Get the reactive power maximum value of the curve (with the possibility of extrapolating slope of reactive
* limits outside active limits)
*/
double getMaxQ(double p, boolean extrapolateReactiveLimitSlope);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import java.util.TreeMap;

/**
*
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
class ReactiveCapabilityCurveImpl implements ReactiveCapabilityCurve {
Expand All @@ -30,6 +29,9 @@ static class PointImpl implements Point {
private double maxQ;

PointImpl(double p, double minQ, double maxQ) {
if (minQ > maxQ) {
vidaldid-rte marked this conversation as resolved.
Show resolved Hide resolved
throw new IllegalArgumentException("minQ should be lower than maxQ");
}
this.p = p;
this.minQ = minQ;
this.maxQ = maxQ;
Expand All @@ -54,10 +56,46 @@ public double getMaxQ() {

private final TreeMap<Double, Point> points;

ReactiveCapabilityCurveImpl(TreeMap<Double, Point> points) {
private static void checkPointsSize(TreeMap<Double, Point> points) {
if (points.size() < 2) {
throw new IllegalStateException("Points size must be >= 2");
throw new IllegalStateException("points size should be >= 2");
}
vidaldid-rte marked this conversation as resolved.
Show resolved Hide resolved
}

private Point extrapolateReactiveLimitsSlope(double p) {
Map.Entry<Double, Point> e1 = points.floorEntry(p);
Map.Entry<Double, Point> e2 = points.ceilingEntry(p);
double minQ;
double maxQ;
if (e1 == null && e2 != null) {
// Extrapolate reactive limits slope below min active power limit p2
Point p2 = e2.getValue();
Point p2bis = points.higherEntry(e2.getKey()).getValue(); // p < p2 < p2bis
double slopeMinQ = (p2bis.getMinQ() - p2.getMinQ()) / (p2bis.getP() - p2.getP());
double slopeMaxQ = (p2bis.getMaxQ() - p2.getMaxQ()) / (p2bis.getP() - p2.getP());
minQ = p2.getMinQ() + slopeMinQ * (p - p2.getP());
maxQ = p2.getMaxQ() + slopeMaxQ * (p - p2.getP());
} else if (e1 != null && e2 == null) {
// Extrapolate reactive limits slope above max active power limit p1
Point p1 = e1.getValue();
Point p1bis = points.lowerEntry(e1.getKey()).getValue(); // p1bis < p1 < p
double slopeMinQ = (p1.getMinQ() - p1bis.getMinQ()) / (p1.getP() - p1bis.getP());
double slopeMaxQ = (p1.getMaxQ() - p1bis.getMaxQ()) / (p1.getP() - p1bis.getP());
minQ = p1.getMinQ() + slopeMinQ * (p - p1.getP());
maxQ = p1.getMaxQ() + slopeMaxQ * (p - p1.getP());
} else {
throw new IllegalStateException();
}
if (minQ <= maxQ) {
return new PointImpl(p, minQ, maxQ);
} else { // Corner case of intersecting reactive limits when extrapolated
double limitQ = (minQ + maxQ) / 2;
return new PointImpl(p, limitQ, limitQ); // Returning the intersection as limits
vidaldid-rte marked this conversation as resolved.
Show resolved Hide resolved
}
}

ReactiveCapabilityCurveImpl(TreeMap<Double, Point> points) {
checkPointsSize(points);
this.points = points;
}

Expand Down Expand Up @@ -88,9 +126,7 @@ public ReactiveLimitsKind getKind() {

@Override
public double getMinQ(double p) {
if (points.size() < 2) {
throw new IllegalStateException("points size should be >= 2");
}
checkPointsSize(points);
Point pt = points.get(p);
if (pt != null) {
return pt.getMinQ();
Expand All @@ -113,9 +149,7 @@ public double getMinQ(double p) {

@Override
public double getMaxQ(double p) {
if (points.size() < 2) {
throw new IllegalStateException("points size should be >= 2");
}
checkPointsSize(points);
Point pt = points.get(p);
if (pt != null) {
return pt.getMaxQ();
Expand All @@ -135,4 +169,22 @@ public double getMaxQ(double p) {
}
}
}

@Override
public double getMinQ(double p, boolean extrapolateReactiveLimitSlope) {
checkPointsSize(points);
SylvestreSakti marked this conversation as resolved.
Show resolved Hide resolved
if (!extrapolateReactiveLimitSlope || p >= points.firstKey() && p <= points.lastKey()) {
return getMinQ(p);
}
return extrapolateReactiveLimitsSlope(p).getMinQ();
}

@Override
public double getMaxQ(double p, boolean extrapolateReactiveLimitSlope) {
checkPointsSize(points);
if (!extrapolateReactiveLimitSlope || p >= points.firstKey() && p <= points.lastKey()) {
SylvestreSakti marked this conversation as resolved.
Show resolved Hide resolved
return getMaxQ(p);
}
return extrapolateReactiveLimitsSlope(p).getMaxQ();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ private ReactiveCapabilityCurveImpl createCurve(Point... points) {
}

@Test
void testInterpolation() {
void testReactiveCapabilityCurve() {
ReactiveCapabilityCurveImpl curve = createCurve(new PointImpl(100.0, 200.0, 300.0),
new PointImpl(200.0, 300.0, 400.0));
// bounds test
Expand All @@ -51,4 +51,38 @@ void testInterpolation() {
assertEquals(400.0, curve.getMaxQ(1000.0), 0.0);
}

private void testReactiveCapabilityCurveWithReactiveLimitsExtrapolation(boolean extrapolate) {
SylvestreSakti marked this conversation as resolved.
Show resolved Hide resolved
ReactiveCapabilityCurveImpl curve = createCurve(new PointImpl(100.0, 200.0, 300.0),
new PointImpl(200.0, 300.0, 400.0),
new PointImpl(300.0, 300.0, 400.0),
new PointImpl(400.0, 310.0, 390.0));
// bounds test
assertEquals(200.0, curve.getMinQ(100.0, extrapolate), 0.0);
assertEquals(300.0, curve.getMaxQ(100.0, extrapolate), 0.0);
assertEquals(300.0, curve.getMinQ(200.0, extrapolate), 0.0);
assertEquals(400.0, curve.getMaxQ(200.0, extrapolate), 0.0);

// interpolation test
assertEquals(250.0, curve.getMinQ(150.0, extrapolate), 0.0);
assertEquals(350.0, curve.getMaxQ(150.0, extrapolate), 0.0);
assertEquals(210.0, curve.getMinQ(110.0, extrapolate), 0.0);
assertEquals(310.0, curve.getMaxQ(110.0, extrapolate), 0.0);

// out of bounds test
assertEquals(extrapolate ? 100.0 : 200.0, curve.getMinQ(0.0, extrapolate), 0.0);
assertEquals(extrapolate ? 200.0 : 300.0, curve.getMaxQ(0.0, extrapolate), 0.0);
assertEquals(extrapolate ? 320.0 : 310.0, curve.getMinQ(500.0, extrapolate), 0.0);
assertEquals(extrapolate ? 380.0 : 390.0, curve.getMaxQ(500.0, extrapolate), 0.0);

// intersecting reactive limits test
assertEquals(extrapolate ? 350.0 : 310.0, curve.getMinQ(1500.0, extrapolate), 0.0);
assertEquals(extrapolate ? 350.0 : 390.0, curve.getMaxQ(1500.0, extrapolate), 0.0);
}

@Test
void testReactiveCapabilityCurveWithReactiveLimitsExtrapolation() {
testReactiveCapabilityCurveWithReactiveLimitsExtrapolation(false);
testReactiveCapabilityCurveWithReactiveLimitsExtrapolation(true);
}

}
Loading