-
Notifications
You must be signed in to change notification settings - Fork 8
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
Fix limits handling in slack distribution on generators #1041
Changes from all commits
271fac8
c1cc537
0b9362a
f1a7faa
ac15815
f7d727e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -334,6 +334,7 @@ private void applyGeneratorChange(LfNetworkParameters networkParameters) { | |
if (!generator.isDisabled()) { | ||
double newTargetP = generatorChange.isRelative() ? generator.getTargetP() + generatorChange.activePowerValue() : generatorChange.activePowerValue(); | ||
generator.setTargetP(newTargetP); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was about to merge the PR but I have a doubt, why you don't change these lines too in a
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checked: indeed the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. indeed, but adding setInitialTargetP(0) would improve code clarity and won't harm, I am adding it. |
||
generator.setInitialTargetP(newTargetP); | ||
if (!AbstractLfGenerator.checkActivePowerControl(generator.getId(), generator.getTargetP(), generator.getMinP(), generator.getMaxP(), | ||
networkParameters.getPlausibleActivePowerLimit(), networkParameters.isUseActiveLimits(), null)) { | ||
generator.setParticipating(false); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,10 @@ | |
|
||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>} | ||
|
@@ -25,15 +28,13 @@ public final class ActivePowerDistribution { | |
*/ | ||
public static final double P_RESIDUE_EPS = Math.pow(10, -5); | ||
|
||
public record StepResult(double done, boolean movedBuses) { } | ||
|
||
public interface Step { | ||
|
||
String getElementType(); | ||
|
||
List<ParticipatingElement> getParticipatingElements(Collection<LfBus> buses); | ||
|
||
StepResult run(List<ParticipatingElement> participatingElements, int iteration, double remainingMismatch); | ||
double run(List<ParticipatingElement> participatingElements, int iteration, double remainingMismatch); | ||
} | ||
|
||
public record Result(int iteration, double remainingMismatch, boolean movedBuses) { } | ||
|
@@ -54,25 +55,26 @@ public Result run(LfNetwork network, double activePowerMismatch) { | |
|
||
public Result run(Collection<LfBus> buses, double activePowerMismatch) { | ||
List<ParticipatingElement> participatingElements = step.getParticipatingElements(buses); | ||
final Map<ParticipatingElement, Double> initialP = participatingElements.stream() | ||
.collect(Collectors.toUnmodifiableMap(Function.identity(), ParticipatingElement::getTargetP)); | ||
|
||
int iteration = 0; | ||
double remainingMismatch = activePowerMismatch; | ||
boolean movedBuses = false; | ||
while (!participatingElements.isEmpty() | ||
&& Math.abs(remainingMismatch) > P_RESIDUE_EPS) { | ||
|
||
if (ParticipatingElement.participationFactorNorm(participatingElements) > 0.0) { | ||
StepResult stepResult = step.run(participatingElements, iteration, remainingMismatch); | ||
remainingMismatch -= stepResult.done(); | ||
if (stepResult.movedBuses()) { | ||
movedBuses = true; | ||
} | ||
double done = step.run(participatingElements, iteration, remainingMismatch); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "powerDistributed" might be a clearer name than "done", don't you agree? |
||
remainingMismatch -= done; | ||
} else { | ||
break; | ||
} | ||
iteration++; | ||
} | ||
|
||
final boolean movedBuses = initialP.entrySet().stream() | ||
.anyMatch(e -> Math.abs(e.getKey().getTargetP() - e.getValue()) > P_RESIDUE_EPS); | ||
|
||
return new Result(iteration, remainingMismatch, movedBuses); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
|
||
import java.util.Collection; | ||
import java.util.Iterator; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
|
@@ -55,16 +56,27 @@ public List<ParticipatingElement> getParticipatingElements(Collection<LfBus> bus | |
.flatMap(bus -> bus.getGenerators().stream()) | ||
.filter(generator -> isParticipating(generator) && getParticipationFactor(generator) != 0) | ||
.map(generator -> new ParticipatingElement(generator, getParticipationFactor(generator))) | ||
.collect(Collectors.toList()); | ||
.collect(Collectors.toCollection(LinkedList::new)); | ||
} | ||
|
||
@Override | ||
public ActivePowerDistribution.StepResult run(List<ParticipatingElement> participatingElements, int iteration, double remainingMismatch) { | ||
public double run(List<ParticipatingElement> participatingElements, int iteration, double remainingMismatch) { | ||
// normalize participation factors at each iteration start as some | ||
// generators might have reach a limit and have been discarded | ||
ParticipatingElement.normalizeParticipationFactors(participatingElements); | ||
|
||
double done = 0d; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same, another name than "done" might be better (even if the name "done" was not introduced in this PR). |
||
double mismatch = remainingMismatch; | ||
if (iteration == 0) { | ||
// "undo" everything from targetP to go back to initialP | ||
for (ParticipatingElement participatingGenerator : participatingElements) { | ||
LfGenerator generator = (LfGenerator) participatingGenerator.getElement(); | ||
done += generator.getInitialTargetP() - generator.getTargetP(); | ||
mismatch -= generator.getInitialTargetP() - generator.getTargetP(); | ||
generator.setTargetP(generator.getInitialTargetP()); | ||
} | ||
} | ||
Comment on lines
+69
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ℹ️ Note to reviewers This is the way I found to fix the issue AND also avoiding a massive refactor: today Note also that such refactoring's will need to be addressed for future developments planned in #979 and powsybl-entsoe/# - planned end of this year / Q4-2024 on our side. |
||
|
||
int modifiedBuses = 0; | ||
int generatorsAtMax = 0; | ||
int generatorsAtMin = 0; | ||
|
@@ -85,12 +97,12 @@ public ActivePowerDistribution.StepResult run(List<ParticipatingElement> partici | |
minP = Math.max(minP, 0); | ||
} | ||
|
||
double newTargetP = targetP + remainingMismatch * factor; | ||
if (remainingMismatch > 0 && newTargetP > maxP) { | ||
double newTargetP = targetP + mismatch * factor; | ||
if (mismatch > 0 && newTargetP > maxP) { | ||
newTargetP = maxP; | ||
generatorsAtMax++; | ||
it.remove(); | ||
} else if (remainingMismatch < 0 && newTargetP < minP) { | ||
} else if (mismatch < 0 && newTargetP < minP) { | ||
newTargetP = minP; | ||
generatorsAtMin++; | ||
it.remove(); | ||
|
@@ -106,10 +118,10 @@ public ActivePowerDistribution.StepResult run(List<ParticipatingElement> partici | |
} | ||
|
||
LOGGER.debug("{} MW / {} MW distributed at iteration {} to {} generators ({} at max power, {} at min power)", | ||
done * PerUnit.SB, remainingMismatch * PerUnit.SB, iteration, modifiedBuses, | ||
done * PerUnit.SB, mismatch * PerUnit.SB, iteration, modifiedBuses, | ||
generatorsAtMax, generatorsAtMin); | ||
|
||
return new ActivePowerDistribution.StepResult(done, modifiedBuses != 0); | ||
return done; | ||
} | ||
|
||
private double getParticipationFactor(LfGenerator generator) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ℹ️ Note to reviewers: This part could be a separate BugFix PR... let me know...
if
slackBusPMaxMismatch
is small in comparison withActivePowerDistribution.P_RESIDUE_EPS
, the outerloop becomes unstable because 1/ActivePowerDistribution
will not distribute anything and 2/ Outerloop says something has to be distributedThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for catching this! I think we can let it in this PR.