Skip to content

Commit

Permalink
Fix Q dispatch not accounting for plausible reactive power range (#1037)
Browse files Browse the repository at this point in the history
Signed-off-by: Damien Jeandemange <damien.jeandemange@artelys.com>
  • Loading branch information
jeandemanged committed May 30, 2024
1 parent 4d7ff9f commit 1cbd341
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -525,18 +525,13 @@ public void addHvdc(LfHvdc hvdc) {
}

private static ToDoubleFunction<String> splitDispatchQ(List<LfGenerator> generatorsWithControl, double qToDispatch) {
// proportional to reactive keys
if (allGeneratorsHaveReactiveKeys(generatorsWithControl)) {
return splitDispatchQWithReactiveKeys(generatorsWithControl, qToDispatch);
}

// fallback on dispatch q proportional to max reactive power range
if (allGeneratorsHavePlausibleReactiveLimits(generatorsWithControl)) {
return splitDispatchQFromMaxReactivePowerRange(generatorsWithControl, qToDispatch);
}

// fall back on dispatch q equally
return splitDispatchQEqually(generatorsWithControl, qToDispatch);
// proportional to reactive keys if possible,
// or else, fallback on dispatch q proportional to max reactive power range if possible,
// or else, fallback on dispatch q equally (always possible)
return splitDispatchQWithReactiveKeys(generatorsWithControl, qToDispatch)
.orElse(splitDispatchQFromMaxReactivePowerRange(generatorsWithControl, qToDispatch)
.orElse(splitDispatchQEqually(generatorsWithControl, qToDispatch))
);
}

private static ToDoubleFunction<String> splitDispatchQEqually(List<LfGenerator> generatorsWithControl, double qToDispatch) {
Expand Down Expand Up @@ -569,14 +564,15 @@ private static ToDoubleFunction<String> splitDispatchQWithEqualProportionOfK(Lis
return qToDispatchByGeneratorId::get;
}

private static ToDoubleFunction<String> splitDispatchQWithReactiveKeys(List<LfGenerator> generatorsWithControl, double qToDispatch) {
private static Optional<ToDoubleFunction<String>> splitDispatchQWithReactiveKeys(List<LfGenerator> generatorsWithControl, double qToDispatch) {
double sumQkeys = 0;
for (LfGenerator generator : generatorsWithControl) {
double qKey = generator.getRemoteControlReactiveKey().orElseThrow();
double qKey = generator.getRemoteControlReactiveKey().orElse(Double.NaN);
sumQkeys += qKey;
}
if (sumQkeys == 0) { // to avoid division by zero
sumQkeys = 1;

if (Double.isNaN(sumQkeys) || sumQkeys == 0.0) {
return Optional.empty();
}

Map<String, Double> qToDispatchByGeneratorId = new HashMap<>(generatorsWithControl.size());
Expand All @@ -585,17 +581,20 @@ private static ToDoubleFunction<String> splitDispatchQWithReactiveKeys(List<LfGe
qToDispatchByGeneratorId.put(generator.getId(), (qKey / sumQkeys) * qToDispatch);
}

return qToDispatchByGeneratorId::get;
return Optional.of(qToDispatchByGeneratorId::get);
}

private static ToDoubleFunction<String> splitDispatchQFromMaxReactivePowerRange(List<LfGenerator> generatorsWithControl, double qToDispatch) {
private static Optional<ToDoubleFunction<String>> splitDispatchQFromMaxReactivePowerRange(List<LfGenerator> generatorsWithControl, double qToDispatch) {
double sumMaxRanges = 0.0;
for (LfGenerator generator : generatorsWithControl) {
if (!generatorHasPlausibleReactiveLimits(generator)) {
return Optional.empty();
}
double maxRangeQ = generator.getRangeQ(LfGenerator.ReactiveRangeMode.MAX);
sumMaxRanges += maxRangeQ;
}
if (sumMaxRanges == 0) { // to avoid division by zero
sumMaxRanges = 1;
if (sumMaxRanges == 0.0) {
return Optional.empty();
}

Map<String, Double> qToDispatchByGeneratorId = new HashMap<>(generatorsWithControl.size());
Expand All @@ -604,27 +603,21 @@ private static ToDoubleFunction<String> splitDispatchQFromMaxReactivePowerRange(
qToDispatchByGeneratorId.put(generator.getId(), (maxRangeQ / sumMaxRanges) * qToDispatch);
}

return qToDispatchByGeneratorId::get;
return Optional.of(qToDispatchByGeneratorId::get);
}

private static boolean allGeneratorsHaveReactiveKeys(List<LfGenerator> generators) {
for (LfGenerator generator : generators) {
double qKey = generator.getRemoteControlReactiveKey().orElse(Double.NaN);
if (Double.isNaN(qKey)) {
return false;
}
}
return true;
private static boolean generatorHasPlausibleReactiveLimits(LfGenerator generator) {
double minQ = generator.getMinQ();
double maxQ = generator.getMaxQ();
double rangeQ = maxQ - minQ;
return Math.abs(minQ) < PLAUSIBLE_REACTIVE_LIMITS &&
Math.abs(maxQ) < PLAUSIBLE_REACTIVE_LIMITS &&
rangeQ > PlausibleValues.MIN_REACTIVE_RANGE / PerUnit.SB &&
rangeQ < PlausibleValues.MAX_REACTIVE_RANGE / PerUnit.SB;
}

private static boolean allGeneratorsHavePlausibleReactiveLimits(List<LfGenerator> generators) {
for (LfGenerator generator : generators) {
if (Math.abs(generator.getMinQ()) > PLAUSIBLE_REACTIVE_LIMITS
|| Math.abs(generator.getMaxQ()) > PLAUSIBLE_REACTIVE_LIMITS) {
return false;
}
}
return true;
return generators.stream().allMatch(AbstractLfBus::generatorHasPlausibleReactiveLimits);
}

protected static double dispatchQ(List<LfGenerator> generatorsWithControl, boolean reactiveLimits,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,19 @@ void testWithMixedGenLoad() {
assertReactivePowerEquals(-120, gen2.getTerminal());
assertReactivePowerEquals(100, ngen2Nhv1.getTerminal1());
}

@Test
void testZeroReactiveRangeAndReactiveLimitsDisabled() {
parameters.setUseReactiveLimits(false);

gen2.newMinMaxReactiveLimits()
.setMinQ(0)
.setMaxQ(0)
.add();
LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
assertReactivePowerEquals(-109.228, gen.getTerminal());
assertReactivePowerEquals(-152.265, gen2.getTerminal());
assertReactivePowerEquals(-199.998, nhv2Nload.getTerminal2());
}
}

0 comments on commit 1cbd341

Please sign in to comment.