Skip to content

Refactor access to branch limits when detecting limit violations #1197

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

Merged
merged 8 commits into from
Apr 9, 2025
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,17 @@ public abstract class AbstractLfBranch extends AbstractElement implements LfBran

protected final LfBus bus2;

private final Map<LimitType, List<LfLimit>> limits1 = new EnumMap<>(LimitType.class);
private List<LfLimit> currentLimits1;

private final Map<LimitType, List<LfLimit>> limits2 = new EnumMap<>(LimitType.class);
private List<LfLimit> activePowerLimits1;

private List<LfLimit> apparentPowerLimits1;

private List<LfLimit> currentLimits2;

private List<LfLimit> activePowerLimits2;

private List<LfLimit> apparentPowerLimits2;

protected final PiModel piModel;

Expand Down Expand Up @@ -61,6 +69,8 @@ static class ZeroImpedanceContext {

protected LfAsymLine asymLine;

private static final String LIMIT_TYPE_UNSUPPORTED_TEMPLATE = "Getting %s limits is not supported.";

protected AbstractLfBranch(LfNetwork network, LfBus bus1, LfBus bus2, PiModel piModel, LfNetworkParameters parameters) {
super(network);
this.bus1 = bus1;
Expand All @@ -87,7 +97,7 @@ public Optional<ThreeSides> getOriginalSide() {
* The resulting list will contain the permanent limit
*/
protected static List<LfLimit> createSortedLimitsList(LoadingLimits loadingLimits, LfBus bus, double[] limitReductions) {
LinkedList<LfLimit> sortedLimits = new LinkedList<>();
List<LfLimit> sortedLimits = new ArrayList<>(3);
if (loadingLimits != null) {
double toPerUnit = getScaleForLimitType(loadingLimits.getLimitType(), bus);

Expand All @@ -99,21 +109,21 @@ protected static List<LfLimit> createSortedLimitsList(LoadingLimits loadingLimit
// https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/CurrentLimits.html
double reduction = limitReductions.length == 0 ? 1d : limitReductions[i + 1]; // Temporary limit's reductions are stored starting from index 1 in `limitReductions`
double originalValuePerUnit = temporaryLimit.getValue() * toPerUnit;
sortedLimits.addFirst(LfLimit.createTemporaryLimit(temporaryLimit.getName(), temporaryLimit.getAcceptableDuration(),
sortedLimits.add(0, LfLimit.createTemporaryLimit(temporaryLimit.getName(), temporaryLimit.getAcceptableDuration(),
originalValuePerUnit, reduction));
}
i++;
}
double reduction = limitReductions.length == 0 ? 1d : limitReductions[0];
sortedLimits.addLast(LfLimit.createPermanentLimit(loadingLimits.getPermanentLimit() * toPerUnit, reduction));
sortedLimits.add(LfLimit.createPermanentLimit(loadingLimits.getPermanentLimit() * toPerUnit, reduction));
}
if (sortedLimits.size() > 1) {
// we only make that fix if there is more than a permanent limit attached to the branch.
for (int i = sortedLimits.size() - 1; i > 0; i--) {
// From the permanent limit to the most serious temporary limit.
sortedLimits.get(i).setAcceptableDuration(sortedLimits.get(i - 1).getAcceptableDuration());
}
sortedLimits.getFirst().setAcceptableDuration(0);
sortedLimits.get(0).setAcceptableDuration(0);
}
return sortedLimits;
}
Expand All @@ -133,22 +143,76 @@ public LfBus getBus2() {
return bus2;
}

private List<LfLimit> getLimits1(LimitType type) {
switch (type) {
case ACTIVE_POWER -> {
return activePowerLimits1;
}
case APPARENT_POWER -> {
return apparentPowerLimits1;
}
case CURRENT -> {
return currentLimits1;
}
default -> throw new UnsupportedOperationException(String.format(LIMIT_TYPE_UNSUPPORTED_TEMPLATE, type.name()));
}
}

private void setLimits1(LimitType type, List<LfLimit> limits) {
switch (type) {
case ACTIVE_POWER -> activePowerLimits1 = limits;
case APPARENT_POWER -> apparentPowerLimits1 = limits;
case CURRENT -> currentLimits1 = limits;
default -> throw new UnsupportedOperationException(String.format(LIMIT_TYPE_UNSUPPORTED_TEMPLATE, type.name()));
}
}

public <T extends LoadingLimits> List<LfLimit> getLimits1(LimitType type, Supplier<Optional<T>> loadingLimitsSupplier, LimitReductionManager limitReductionManager) {
// It is possible to apply the reductions here since the only supported ContingencyContext for LimitReduction is ALL.
return limits1.computeIfAbsent(type, v -> {
var limits = getLimits1(type);
if (limits == null) {
// It is possible to apply the reductions here since the only supported ContingencyContext for LimitReduction is ALL.
var loadingLimits = loadingLimitsSupplier.get().orElse(null);
return createSortedLimitsList(loadingLimits, bus1,
limits = createSortedLimitsList(loadingLimits, bus1,
getLimitReductions(TwoSides.ONE, limitReductionManager, loadingLimits));
});
setLimits1(type, limits);
}
return limits;
}

private List<LfLimit> getLimits2(LimitType type) {
switch (type) {
case ACTIVE_POWER -> {
return activePowerLimits2;
}
case APPARENT_POWER -> {
return apparentPowerLimits2;
}
case CURRENT -> {
return currentLimits2;
}
default -> throw new UnsupportedOperationException(String.format(LIMIT_TYPE_UNSUPPORTED_TEMPLATE, type.name()));
}
}

private void setLimits2(LimitType type, List<LfLimit> limits) {
switch (type) {
case ACTIVE_POWER -> activePowerLimits2 = limits;
case APPARENT_POWER -> apparentPowerLimits2 = limits;
case CURRENT -> currentLimits2 = limits;
default -> throw new UnsupportedOperationException(String.format(LIMIT_TYPE_UNSUPPORTED_TEMPLATE, type.name()));
}
}

public <T extends LoadingLimits> List<LfLimit> getLimits2(LimitType type, Supplier<Optional<T>> loadingLimitsSupplier, LimitReductionManager limitReductionManager) {
// It is possible to apply the reductions here since the only supported ContingencyContext for LimitReduction is ALL.
return limits2.computeIfAbsent(type, v -> {
var limits = getLimits2(type);
if (limits == null) {
// It is possible to apply the reductions here since the only supported ContingencyContext for LimitReduction is ALL.
var loadingLimits = loadingLimitsSupplier.get().orElse(null);
return createSortedLimitsList(loadingLimits, bus2,
limits = createSortedLimitsList(loadingLimits, bus2,
getLimitReductions(TwoSides.TWO, limitReductionManager, loadingLimits));
});
setLimits2(type, limits);
}
return limits;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,14 +234,13 @@ public List<BranchResult> createBranchResult(double preContingencyBranchP1, doub

@Override
public List<LfLimit> getLimits1(final LimitType type, LimitReductionManager limitReductionManager) {
var branch = getBranch();
switch (type) {
case ACTIVE_POWER:
return getLimits1(type, branch::getActivePowerLimits1, limitReductionManager);
return getLimits1(type, () -> getBranch().getActivePowerLimits1(), limitReductionManager);
case APPARENT_POWER:
return getLimits1(type, branch::getApparentPowerLimits1, limitReductionManager);
return getLimits1(type, () -> getBranch().getApparentPowerLimits1(), limitReductionManager);
case CURRENT:
return getLimits1(type, branch::getCurrentLimits1, limitReductionManager);
return getLimits1(type, () -> getBranch().getCurrentLimits1(), limitReductionManager);
case VOLTAGE:
default:
throw new UnsupportedOperationException(String.format("Getting %s limits is not supported.", type.name()));
Expand All @@ -250,14 +249,13 @@ public List<LfLimit> getLimits1(final LimitType type, LimitReductionManager limi

@Override
public List<LfLimit> getLimits2(final LimitType type, LimitReductionManager limitReductionManager) {
var branch = getBranch();
switch (type) {
case ACTIVE_POWER:
return getLimits2(type, branch::getActivePowerLimits2, limitReductionManager);
return getLimits2(type, () -> getBranch().getActivePowerLimits2(), limitReductionManager);
case APPARENT_POWER:
return getLimits2(type, branch::getApparentPowerLimits2, limitReductionManager);
return getLimits2(type, () -> getBranch().getApparentPowerLimits2(), limitReductionManager);
case CURRENT:
return getLimits2(type, branch::getCurrentLimits2, limitReductionManager);
return getLimits2(type, () -> getBranch().getCurrentLimits2(), limitReductionManager);
case VOLTAGE:
default:
throw new UnsupportedOperationException(String.format("Getting %s limits is not supported.", type.name()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,13 @@ public List<BranchResult> createBranchResult(double preContingencyBranchP1, doub

@Override
public List<LfLimit> getLimits1(final LimitType type, LimitReductionManager limitReductionManager) {
var danglingLine = getDanglingLine();
switch (type) {
case ACTIVE_POWER:
return getLimits1(type, danglingLine::getActivePowerLimits, limitReductionManager);
return getLimits1(type, () -> getDanglingLine().getActivePowerLimits(), limitReductionManager);
case APPARENT_POWER:
return getLimits1(type, danglingLine::getApparentPowerLimits, limitReductionManager);
return getLimits1(type, () -> getDanglingLine().getApparentPowerLimits(), limitReductionManager);
case CURRENT:
return getLimits1(type, danglingLine::getCurrentLimits, limitReductionManager);
return getLimits1(type, () -> getDanglingLine().getCurrentLimits(), limitReductionManager);
case VOLTAGE:
default:
throw new UnsupportedOperationException(String.format("Getting %s limits is not supported.", type.name()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,13 @@ public List<BranchResult> createBranchResult(double preContingencyBranchP1, doub

@Override
public List<LfLimit> getLimits1(final LimitType type, LimitReductionManager limitReductionManager) {
var leg = getLeg();
switch (type) {
case ACTIVE_POWER:
return getLimits1(type, leg::getActivePowerLimits, limitReductionManager);
return getLimits1(type, () -> getLeg().getActivePowerLimits(), limitReductionManager);
case APPARENT_POWER:
return getLimits1(type, leg::getApparentPowerLimits, limitReductionManager);
return getLimits1(type, () -> getLeg().getApparentPowerLimits(), limitReductionManager);
case CURRENT:
return getLimits1(type, leg::getCurrentLimits, limitReductionManager);
return getLimits1(type, () -> getLeg().getCurrentLimits(), limitReductionManager);
case VOLTAGE:
default:
throw new UnsupportedOperationException(String.format("Getting %s limits is not supported.", type.name()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,39 @@ private void addVoltageAngleLimitViolation(LimitViolation limitViolation, LfNetw
addLimitViolation(limitViolation, voltageAngleLimit.getId());
}

private void detectBranchCurrentViolations(LfBranch branch, LfBus bus, Function<LfBranch, Evaluable> iGetter, List<LfBranch.LfLimit> limits, TwoSides side) {
double i = iGetter.apply(branch).eval();
for (LfBranch.LfLimit temporaryLimit : limits) {
if (i > temporaryLimit.getReducedValue()) {
addBranchLimitViolation(createLimitViolation(branch, temporaryLimit, LimitViolationType.CURRENT, PerUnit.ib(bus.getNominalV()), i, side));
break;
}
}
}

private void detectBranchActivePowerViolations(LfBranch branch, Function<LfBranch, Evaluable> pGetter, List<LfBranch.LfLimit> limits, TwoSides side) {
double p = pGetter.apply(branch).eval();
for (LfBranch.LfLimit temporaryLimit : limits) {
if (Math.abs(p) > temporaryLimit.getReducedValue()) {
addBranchLimitViolation(createLimitViolation(branch, temporaryLimit, LimitViolationType.ACTIVE_POWER, PerUnit.SB, p, side));
break;
}
}
}

private void detectBranchApparentPowerViolations(LfBranch branch, ToDoubleFunction<LfBranch> sGetter, List<LfBranch.LfLimit> limits, TwoSides side) {
//Apparent power is not relevant for fictitious branches and may be NaN
double s = sGetter.applyAsDouble(branch);
if (!Double.isNaN(s)) {
for (LfBranch.LfLimit temporaryLimit : limits) {
if (s > temporaryLimit.getReducedValue()) {
addBranchLimitViolation(createLimitViolation(branch, temporaryLimit, LimitViolationType.APPARENT_POWER, PerUnit.SB, s, side));
break;
}
}
}
}

private void detectBranchSideViolations(LfBranch branch, LfBus bus,
TriFunction<LfBranch, LimitType, LimitReductionManager, List<LfBranch.LfLimit>> limitsGetter,
Function<LfBranch, Evaluable> iGetter,
Expand All @@ -120,35 +153,17 @@ private void detectBranchSideViolations(LfBranch branch, LfBus bus,
TwoSides side) {
List<LfBranch.LfLimit> limits = limitsGetter.apply(branch, LimitType.CURRENT, limitReductionManager);
if (!limits.isEmpty()) {
double i = iGetter.apply(branch).eval();
limits.stream()
.filter(temporaryLimit -> i > temporaryLimit.getReducedValue())
.findFirst()
.map(temporaryLimit -> createLimitViolation(branch, temporaryLimit, LimitViolationType.CURRENT, PerUnit.ib(bus.getNominalV()), i, side))
.ifPresent(this::addBranchLimitViolation);
detectBranchCurrentViolations(branch, bus, iGetter, limits, side);
}

limits = limitsGetter.apply(branch, LimitType.ACTIVE_POWER, limitReductionManager);
if (!limits.isEmpty()) {
double p = pGetter.apply(branch).eval();
limits.stream()
.filter(temporaryLimit -> Math.abs(p) > temporaryLimit.getReducedValue())
.findFirst()
.map(temporaryLimit -> createLimitViolation(branch, temporaryLimit, LimitViolationType.ACTIVE_POWER, PerUnit.SB, p, side))
.ifPresent(this::addBranchLimitViolation);
detectBranchActivePowerViolations(branch, pGetter, limits, side);
}

limits = limitsGetter.apply(branch, LimitType.APPARENT_POWER, limitReductionManager);
if (!limits.isEmpty()) {
//Apparent power is not relevant for fictitious branches and may be NaN
double s = sGetter.applyAsDouble(branch);
if (!Double.isNaN(s)) {
limits.stream()
.filter(temporaryLimit -> s > temporaryLimit.getReducedValue())
.findFirst()
.map(temporaryLimit -> createLimitViolation(branch, temporaryLimit, LimitViolationType.APPARENT_POWER, PerUnit.SB, s, side))
.ifPresent(this::addBranchLimitViolation);
}
detectBranchApparentPowerViolations(branch, sGetter, limits, side);
}
}

Expand Down