LoadingLimitsUtilTest.java
/**
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.iidm.network.util;
import com.powsybl.iidm.network.LoadingLimits;
import com.powsybl.iidm.network.LoadingLimitsAdder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import static java.lang.Integer.MAX_VALUE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author Olivier Perrin {@literal <olivier.perrin at rte-france.com>}
*/
class LoadingLimitsUtilTest {
@ParameterizedTest()
@MethodSource("getDirectCallFromAdderParameters")
void computeAs100PercentOfFirstTemporaryLimitTest(boolean directCallFromAdder) {
TemporaryLimitToCreate tl1 = new TemporaryLimitToCreate("5'", 300, 1000.);
TemporaryLimitToCreate tl2 = new TemporaryLimitToCreate("1'", 60, 1200.);
LimitsAdder<?, ?> adder = new LimitsAdder<>("ownerId", Double.NaN, List.of(tl1, tl2));
if (directCallFromAdder) {
adder.fixLimits();
} else {
LoadingLimitsUtil.fixMissingPermanentLimit(adder, 100.);
}
assertEquals(1000., adder.getPermanentLimit(), 0.001);
assertEquals(2, adder.getTemporaryLimitToCreateList().size());
List<TemporaryLimitToCreate> temps = adder.getTemporaryLimitToCreateList().stream().toList();
assertEquals(1000., temps.get(0).value());
assertEquals(1200., temps.get(1).value());
}
@ParameterizedTest()
@MethodSource("getDirectCallFromAdderParameters")
void computeAsCustomPercentageOfFirstTemporaryLimitTest(boolean directCallFromAdder) {
TemporaryLimitToCreate tl1 = new TemporaryLimitToCreate("5'", 300, 1000.);
TemporaryLimitToCreate tl2 = new TemporaryLimitToCreate("1'", 60, 1200.);
LimitsAdder<?, ?> adder = new LimitsAdder<>("ownerId", Double.NaN, List.of(tl1, tl2));
CustomLogger customLogger = new CustomLogger();
if (directCallFromAdder) {
adder.fixLimits(90., customLogger);
} else {
LoadingLimitsUtil.fixMissingPermanentLimit(adder, 90., adder.getOwnerId(), customLogger);
}
// The percentage is applied
assertEquals(900., adder.getPermanentLimit(), 0.001);
assertEquals(2, adder.getTemporaryLimitToCreateList().size());
List<TemporaryLimitToCreate> temps = adder.getTemporaryLimitToCreateList().stream().toList();
assertEquals(1000., temps.get(0).value());
assertEquals(1200., temps.get(1).value());
LoggedData loggedData = customLogger.getLoggedData();
assertEquals("Operational Limits of ownerId", loggedData.what());
assertEquals("Operational limits without permanent limit is considered with permanent limit " +
"equal to lowest temporary limit value weighted by a coefficient of 0.9.", loggedData.reason());
assertTrue(Double.isNaN(loggedData.wrongValue()));
assertEquals(900., loggedData.fixedValue(), 0.001);
}
static Stream<Arguments> getDirectCallFromAdderParameters() {
return Stream.of(
Arguments.of(Boolean.FALSE),
Arguments.of(Boolean.TRUE)
);
}
@Test
void computeWithTemporaryLimitWithInfiniteAcceptableDurationTest() {
TemporaryLimitToCreate tl1 = new TemporaryLimitToCreate("Infinite", MAX_VALUE, 1000.);
TemporaryLimitToCreate tl2 = new TemporaryLimitToCreate("1'", 60, 1200.);
LimitsAdder<?, ?> adder = new LimitsAdder<>("ownerId", Double.NaN, List.of(tl1, tl2));
CustomLogger customLogger = new CustomLogger();
LoadingLimitsUtil.fixMissingPermanentLimit(adder, 75., adder.getOwnerId(), customLogger);
// The permanent limit is set with the value of temporary limit of infinite acceptable duration.
// The percentage is not applied and the said temporary limit is removed from the temporary limits list.
assertEquals(1000., adder.getPermanentLimit(), 0.001);
assertEquals(1, adder.getTemporaryLimitToCreateList().size());
List<TemporaryLimitToCreate> temps = adder.getTemporaryLimitToCreateList().stream().toList();
assertEquals(1200., temps.get(0).value());
LoggedData loggedData = customLogger.getLoggedData();
assertEquals("Operational Limits of ownerId", loggedData.what());
assertEquals("Operational limits without permanent limit is considered with permanent limit " +
"equal to lowest temporary limit value with infinite acceptable duration", loggedData.reason());
assertTrue(Double.isNaN(loggedData.wrongValue()));
assertEquals(1000., loggedData.fixedValue(), 0.001);
}
private record TemporaryLimitToCreate(String name, int acceptableDuration, double value) {
}
private static class LimitsAdder<L extends LoadingLimits, A extends LimitsAdder<L, A>> implements LoadingLimitsAdder<L, A> {
private final String ownerId;
private final List<TemporaryLimitToCreate> temporaryLimitToCreateList;
private double permanentLimit;
public LimitsAdder(String ownerId, double permanentLimit, List<TemporaryLimitToCreate> temporaryLimitToCreateList) {
this.ownerId = ownerId;
this.permanentLimit = permanentLimit;
this.temporaryLimitToCreateList = new ArrayList<>(temporaryLimitToCreateList);
}
public List<TemporaryLimitToCreate> getTemporaryLimitToCreateList() {
return this.temporaryLimitToCreateList;
}
@Override
public A setPermanentLimit(double limit) {
this.permanentLimit = limit;
return (A) this;
}
@Override
public TemporaryLimitAdder<A> beginTemporaryLimit() {
return null;
}
@Override
public double getPermanentLimit() {
return this.permanentLimit;
}
@Override
public double getTemporaryLimitValue(int acceptableDuration) {
return temporaryLimitToCreateList.stream()
.filter(t -> t.acceptableDuration() == acceptableDuration)
.map(TemporaryLimitToCreate::value)
.findFirst().orElse(Double.NaN);
}
private Optional<TemporaryLimitToCreate> getTemporaryLimitByName(String name) {
return temporaryLimitToCreateList.stream()
.filter(t -> t.name().equals(name))
.findFirst();
}
@Override
public double getTemporaryLimitValue(String name) {
return getTemporaryLimitByName(name)
.map(TemporaryLimitToCreate::value)
.orElse(Double.NaN);
}
@Override
public int getTemporaryLimitAcceptableDuration(String name) {
return getTemporaryLimitByName(name)
.map(TemporaryLimitToCreate::acceptableDuration)
.orElse(MAX_VALUE);
}
@Override
public double getLowestTemporaryLimitValue() {
return temporaryLimitToCreateList.stream().map(TemporaryLimitToCreate::value).min(Double::compareTo).orElse(Double.NaN);
}
@Override
public boolean hasTemporaryLimits() {
return !temporaryLimitToCreateList.isEmpty();
}
@Override
public Collection<String> getTemporaryLimitNames() {
return temporaryLimitToCreateList.stream().map(TemporaryLimitToCreate::name).toList();
}
@Override
public void removeTemporaryLimit(String name) {
temporaryLimitToCreateList.removeIf(temporaryLimitToCreate -> temporaryLimitToCreate.name().equals(name));
}
@Override
public String getOwnerId() {
return this.ownerId;
}
@Override
public L add() {
return null;
}
}
private record LoggedData(String what, String reason, double wrongValue, double fixedValue) {
}
private static class CustomLogger implements LoadingLimitsUtil.LimitFixLogger {
private LoggedData loggedData;
public void log(String what, String reason, double wrongValue, double fixedValue) {
loggedData = new LoggedData(what, reason, wrongValue, fixedValue);
}
public LoggedData getLoggedData() {
return loggedData;
}
}
}