Skip to content

Commit

Permalink
Permanent limit is now mandatory in IIDM (#2636)
Browse files Browse the repository at this point in the history
Signed-off-by: Olivier Perrin <olivier.perrin@rte-france.com>
Co-authored-by: Sophie Frasnedo <sophie.frasnedo@rte-france.com>
  • Loading branch information
annetill and So-Fras authored Jan 16, 2024
1 parent e148a02 commit c422777
Show file tree
Hide file tree
Showing 40 changed files with 1,170 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,14 @@ private Conversion.Config config(Properties p) {
getFormat(),
p,
DISCONNECT_DANGLING_LINE_IF_BOUNDARY_SIDE_IS_DISCONNECTED_PARAMETER,
defaultValueConfig))
.setMissingPermanentLimitPercentage(
Parameter.readDouble(
getFormat(),
p,
MISSING_PERMANENT_LIMIT_PERCENTAGE_PARAMETER,
defaultValueConfig));

String namingStrategy = Parameter.readString(getFormat(), p, NAMING_STRATEGY_PARAMETER, defaultValueConfig);

// Build the naming strategy with the default uuid namespace for creating name-based uuids
Expand Down Expand Up @@ -554,6 +561,7 @@ private void copyStream(ReadOnlyDataSource from, DataSource to, String fromName,
public static final String STORE_CGMES_CONVERSION_CONTEXT_AS_NETWORK_EXTENSION = "iidm.import.cgmes.store-cgmes-conversion-context-as-network-extension";
public static final String IMPORT_NODE_BREAKER_AS_BUS_BREAKER = "iidm.import.cgmes.import-node-breaker-as-bus-breaker";
public static final String DISCONNECT_DANGLING_LINE_IF_BOUNDARY_SIDE_IS_DISCONNECTED = "iidm.import.cgmes.disconnect-dangling-line-if-boundary-side-is-disconnected";
public static final String MISSING_PERMANENT_LIMIT_PERCENTAGE = "iidm.import.cgmes.missing-permanent-limit-percentage";
public static final String IMPORT_CGM_WITH_SUBNETWORKS = "iidm.import.cgmes.cgm-with-subnetworks";
public static final String IMPORT_CGM_WITH_SUBNETWORKS_DEFINED_BY = "iidm.import.cgmes.cgm-with-subnetworks-defined-by";

Expand Down Expand Up @@ -674,6 +682,12 @@ private void copyStream(ReadOnlyDataSource from, DataSource to, String fromName,
SubnetworkDefinedBy.MODELING_AUTHORITY.name(),
Arrays.stream(SubnetworkDefinedBy.values()).map(Enum::name).collect(Collectors.toList()));

public static final Parameter MISSING_PERMANENT_LIMIT_PERCENTAGE_PARAMETER = new Parameter(
MISSING_PERMANENT_LIMIT_PERCENTAGE,
ParameterType.DOUBLE,
"Percentage applied to lowest TATL limit to use as PATL when PATL is missing",
100.);

private static final List<Parameter> STATIC_PARAMETERS = List.of(
ALLOW_UNSUPPORTED_TAP_CHANGERS_PARAMETER,
CHANGE_SIGN_FOR_SHUNT_REACTIVE_POWER_FLOW_INITIAL_STATE_PARAMETER,
Expand All @@ -694,7 +708,8 @@ private void copyStream(ReadOnlyDataSource from, DataSource to, String fromName,
IMPORT_NODE_BREAKER_AS_BUS_BREAKER_PARAMETER,
DISCONNECT_DANGLING_LINE_IF_BOUNDARY_SIDE_IS_DISCONNECTED_PARAMETER,
IMPORT_CGM_WITH_SUBNETWORKS_PARAMETER,
IMPORT_CGM_WITH_SUBNETWORKS_DEFINED_BY_PARAMETER);
IMPORT_CGM_WITH_SUBNETWORKS_DEFINED_BY_PARAMETER,
MISSING_PERMANENT_LIMIT_PERCENTAGE_PARAMETER);

private final Parameter boundaryLocationParameter;
private final Parameter preProcessorsParameter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,18 @@ public void setXfmr3StructuralRatio(Xfmr3StructuralRatioInterpretationAlternativ
xfmr3StructuralRatio = alternative;
}

public double getMissingPermanentLimitPercentage() {
return missingPermanentLimitPercentage;
}

public Config setMissingPermanentLimitPercentage(double missingPermanentLimitPercentage) {
if (missingPermanentLimitPercentage < 0 || missingPermanentLimitPercentage > 100) {
throw new IllegalArgumentException("Missing permanent limit percentage must be between 0 and 100.");
}
this.missingPermanentLimitPercentage = missingPermanentLimitPercentage;
return this;
}

public CgmesImport.FictitiousSwitchesCreationMode getCreateFictitiousSwitchesForDisconnectedTerminalsMode() {
return createFictitiousSwitchesForDisconnectedTerminalsMode;
}
Expand Down Expand Up @@ -1018,6 +1030,8 @@ public boolean disconnectNetworkSideOfDanglingLinesIfBoundaryIsDisconnected() {
private Xfmr3RatioPhaseInterpretationAlternative xfmr3RatioPhase = Xfmr3RatioPhaseInterpretationAlternative.NETWORK_SIDE;
private Xfmr3ShuntInterpretationAlternative xfmr3Shunt = Xfmr3ShuntInterpretationAlternative.NETWORK_SIDE;
private Xfmr3StructuralRatioInterpretationAlternative xfmr3StructuralRatio = Xfmr3StructuralRatioInterpretationAlternative.STAR_BUS_SIDE;

private double missingPermanentLimitPercentage = 100;
}

private final CgmesModel cgmes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
*/
package com.powsybl.cgmes.conversion;

import com.google.common.collect.Iterables;
import com.powsybl.iidm.network.LoadingLimits;
import com.powsybl.iidm.network.LoadingLimitsAdder;

import java.util.HashMap;
Expand All @@ -34,15 +32,9 @@ public class LoadingLimitsMapping {
void addAll() {
for (Map.Entry<String, LoadingLimitsAdder<?, ?>> entry : adders.entrySet()) {
if (!Double.isNaN(entry.getValue().getPermanentLimit()) || entry.getValue().hasTemporaryLimits()) {
LoadingLimits limits = entry.getValue().add();
if (Double.isNaN(limits.getPermanentLimit())) {
double fixedPermanentLimit = Iterables.get(limits.getTemporaryLimits(), 0).getValue();
context.fixed("Operational Limit Set of " + entry.getKey(),
"An operational limit set without permanent limit is considered with permanent limit" +
"equal to lowest TATL value",
Double.NaN, fixedPermanentLimit);
limits.setPermanentLimit(fixedPermanentLimit);
}
entry.getValue()
.fixLimits(context.config().getMissingPermanentLimitPercentage(), context::fixed)
.add();
}
}
adders.clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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.io.IOException;
import java.nio.file.FileSystem;
Expand All @@ -40,6 +43,7 @@
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.powsybl.iidm.network.PhaseTapChanger.RegulationMode.CURRENT_LIMITER;
import static com.powsybl.iidm.network.StaticVarCompensator.RegulationMode.*;
Expand Down Expand Up @@ -362,15 +366,30 @@ void microBEMissingShuntRegulatingControlId() {
assertEquals(shunt.getTerminal(), shunt.getRegulatingTerminal());
}

@Test
void microBEUndefinedPatl() {
Network network = new CgmesImport().importData(CgmesConformity1ModifiedCatalog.microGridBaseCaseBEUndefinedPatl().dataSource(),
@ParameterizedTest(name = "{1}")
@MethodSource("getMicroBEUndefinedPatlParameters")
void microBEUndefinedPatl(double expectedPermanentLimitValue, Double percentage) {
InMemoryPlatformConfig platformConfig = new InMemoryPlatformConfig(fileSystem);
if (percentage != null) {
platformConfig.createModuleConfig("import-export-parameters-default-value")
.setStringProperty(CgmesImport.MISSING_PERMANENT_LIMIT_PERCENTAGE, percentage.toString());
}

Network network = new CgmesImport(platformConfig).importData(CgmesConformity1ModifiedCatalog.microGridBaseCaseBEUndefinedPatl().dataSource(),
NetworkFactory.findDefault(), importParams);
Line line = network.getLine("ffbabc27-1ccd-4fdc-b037-e341706c8d29");
CurrentLimits limits = line.getCurrentLimits1().orElse(null);
assertNotNull(limits);
assertEquals(2, limits.getTemporaryLimits().size());
assertEquals(1312.0, limits.getPermanentLimit(), 0.0);
assertEquals(expectedPermanentLimitValue, limits.getPermanentLimit(), 0.001);
}

static Stream<Arguments> getMicroBEUndefinedPatlParameters() {
return Stream.of(
Arguments.of(1312., null),
Arguments.of(1312., 100.),
Arguments.of(984., 75.)
);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
*/
package com.powsybl.iidm.network;

import com.powsybl.iidm.network.util.LoadingLimitsUtil;

import java.util.Collection;

/**
*
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
Expand All @@ -31,9 +35,100 @@ interface TemporaryLimitAdder<A> {

TemporaryLimitAdder<A> beginTemporaryLimit();

/**
* <p>Get the permanent limit to add.</p>
* <p>This method is useful to fix permanent or temporary limits before calling {@link #add()}.</p>
* @return the permanent limit
*/
double getPermanentLimit();

/**
* <p>Get the temporary limit value corresponding to the given acceptable duration.</p>
* <p>This method is useful to fix permanent or temporary limits before calling {@link #add()}.</p>
* @param acceptableDuration the acceptable duration
* @return the corresponding temporary limit value, or <code>Double.NaN</code> if none is defined.
*/
double getTemporaryLimitValue(int acceptableDuration);

/**
* <p>Get the temporary limit value corresponding to the given name.</p>
* <p>This method is useful to fix permanent or temporary limits before calling {@link #add()}.</p>
* @param name the temporary limit name
* @return the corresponding temporary limit value, or <code>Double.NaN</code> if none is defined.
*/
double getTemporaryLimitValue(String name);

/**
* <p>Get the temporary limit value corresponding to the given name.</p>
* <p>This method is useful to fix permanent or temporary limits before calling {@link #add()}.</p>
* @param name the temporary limit name
* @return the corresponding temporary limit value, or <code>Integer.MAX_VALUE</code> if none is defined.
*/
int getTemporaryLimitAcceptableDuration(String name);

/**
* <p>Get the lowest value of the temporary limits to create.</p>
* <p>This method is useful to fix permanent or temporary limits before calling {@link #add()}.</p>
* @return the lowest temporary limit value, or <code>Double.NaN</code> if no temporary limits are defined.
*/
double getLowestTemporaryLimitValue();

/**
* <p>Indicate if temporary limits to create are defined in the adder.</p>
* <p>This method is useful to fix permanent or temporary limits before calling {@link #add()}.</p>
* @return <code>true</code> if temporary limits to create are defined, <code>false</code> otherwise.
*/
boolean hasTemporaryLimits();

/**
* <p>Get the names of the temporary limits to create.</p>
* <p>This method is useful to fix permanent or temporary limits before calling {@link #add()}.</p>
* @return a collection containing the names of the defined temporary limits.
*/
Collection<String> getTemporaryLimitNames();

/**
* <p>Remove from the temporary limits to create the one(s) corresponding to the given name.</p>
* <p>This method doesn't throw any <code>Exception</code> if no corresponding temporary limits are found.</p>
* <p>This method is useful to fix permanent or temporary limits before calling {@link #add()}.</p>
* @param name a temporary limit name
*/
void removeTemporaryLimit(String name);

/**
* <p>Get the id of the network element on which the LoadingLimits should be added.</p>
* @return the id of the owner network element
*/
String getOwnerId();

/**
* <p>Fix the limits to create if needed, especially the permanent limit when it is not defined (equals <code>Double.NaN</code>).</p>
* @return the adder
* @see LoadingLimitsAdder#fixLimits(double, LoadingLimitsUtil.LimitFixLogger)
*/
default A fixLimits() {
return fixLimits(100., LoadingLimitsUtil.LimitFixLogger.NO_OP);
}

/**
* <p>Fix the limits to create if needed, especially the permanent limit when it is not defined (equals <code>Double.NaN</code>).</p>
* @param missingPermanentLimitPercentage the percentage to use to compute the permanentLimit if it is not defined.
* @return the adder
* @see LoadingLimitsAdder#fixLimits(double, LoadingLimitsUtil.LimitFixLogger)
*/
default A fixLimits(double missingPermanentLimitPercentage) {
return fixLimits(missingPermanentLimitPercentage, LoadingLimitsUtil.LimitFixLogger.NO_OP);
}

/**
* <p>Fix the limits to create if needed, especially the permanent limit when it is not defined (equals <code>Double.NaN</code>).</p>
* @param missingPermanentLimitPercentage the percentage to use to compute the permanentLimit if it is not defined.
* @param limitFixLogger a logger allowing to report the changes applied when fixing the permanent limit.
* @return the adder
* @see LoadingLimitsUtil#fixMissingPermanentLimit(LoadingLimitsAdder, double, String, LoadingLimitsUtil.LimitFixLogger)
*/
default A fixLimits(double missingPermanentLimitPercentage, LoadingLimitsUtil.LimitFixLogger limitFixLogger) {
LoadingLimitsUtil.fixMissingPermanentLimit(this, missingPermanentLimitPercentage, getOwnerId(), limitFixLogger);
return (A) this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* 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;

Expand Down Expand Up @@ -590,9 +591,8 @@ public static void checkConnected(Validable validable, Boolean connected) {
}
}

public static void checkPermanentLimit(Validable validable, double permanentLimit) {
// TODO: if (Double.isNaN(permanentLimit) || permanentLimit <= 0) {
if (permanentLimit <= 0) {
public static void checkPermanentLimit(Validable validable, double permanentLimit, Collection<LoadingLimits.TemporaryLimit> temporaryLimits) {
if (Double.isNaN(permanentLimit) && !temporaryLimits.isEmpty() || permanentLimit <= 0) {
throw new ValidationException(validable, "permanent limit must be defined and be > 0");
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* 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 static java.lang.Integer.MAX_VALUE;

/**
* @author Olivier Perrin {@literal <olivier.perrin at rte-france.com>}
*/
public final class LoadingLimitsUtil {

private LoadingLimitsUtil() {
}

/**
* Interface for objects used to report the performed operation on limits when fixed by
* {@link #fixMissingPermanentLimit(LoadingLimitsAdder, double, String, LimitFixLogger)}.
*/
public interface LimitFixLogger {
LimitFixLogger NO_OP = (what, reason, wrongValue, fixedValue) -> { };

void log(String what, String reason, double wrongValue, double fixedValue);
}

/**
* <p>Compute a missing permanent limit accordingly to the temporary limits and to a given percentage.</p>
* @param limitsAdder the LoadingLimitsAdder which permanent limit should be fixed
* @param missingPermanentLimitPercentage The percentage to apply
*/
public static <L extends LoadingLimits, A extends LoadingLimitsAdder<L, A>> void fixMissingPermanentLimit(LoadingLimitsAdder<L, A> limitsAdder,
double missingPermanentLimitPercentage) {
fixMissingPermanentLimit(limitsAdder, missingPermanentLimitPercentage, "", LimitFixLogger.NO_OP);
}

/**
* <p>Compute a missing permanent limit accordingly to the temporary limits and to a given percentage.</p>
* @param adder the LoadingLimitsAdder which permanent limit should be fixed
* @param missingPermanentLimitPercentage The percentage to apply
* @param ownerId id of the limits' network element. It is only used for reporting purposes.
* @param limitFixLogger the object used to report the performed operation on the permanent limit.
*/
public static <L extends LoadingLimits, A extends LoadingLimitsAdder<L, A>> void fixMissingPermanentLimit(LoadingLimitsAdder<L, A> adder, double missingPermanentLimitPercentage,
String ownerId, LimitFixLogger limitFixLogger) {
if (!Double.isNaN(adder.getPermanentLimit())) {
return;
}

double lowestTemporaryLimitWithInfiniteAcceptableDuration = MAX_VALUE;
boolean hasTemporaryLimitWithInfiniteAcceptableDuration = false;
for (String name : adder.getTemporaryLimitNames()) {
if (adder.getTemporaryLimitAcceptableDuration(name) == MAX_VALUE) {
hasTemporaryLimitWithInfiniteAcceptableDuration = true;
lowestTemporaryLimitWithInfiniteAcceptableDuration =
Math.min(lowestTemporaryLimitWithInfiniteAcceptableDuration, adder.getTemporaryLimitValue(name));
adder.removeTemporaryLimit(name);
}
}

if (hasTemporaryLimitWithInfiniteAcceptableDuration) {
limitFixLogger.log("Operational Limits of " + ownerId,
"Operational limits without permanent limit is considered with permanent limit " +
"equal to lowest temporary limit value with infinite acceptable duration",
Double.NaN, lowestTemporaryLimitWithInfiniteAcceptableDuration);
adder.setPermanentLimit(lowestTemporaryLimitWithInfiniteAcceptableDuration);
} else {
double firstTemporaryLimit = adder.getLowestTemporaryLimitValue();
double percentage = missingPermanentLimitPercentage / 100.;
double fixedPermanentLimit = firstTemporaryLimit * percentage;
limitFixLogger.log("Operational Limits of " + ownerId,
"Operational limits without permanent limit is considered with permanent limit " +
"equal to lowest temporary limit value weighted by a coefficient of " + percentage + ".",
Double.NaN, fixedPermanentLimit);
adder.setPermanentLimit(fixedPermanentLimit);
}
}
}
Loading

0 comments on commit c422777

Please sign in to comment.