Skip to content
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

Improve CGMES import and export of OperationalLimits #3139

Merged
merged 18 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f2c2560
Create an OperationalLimitsGroup for each combination of OperationalL…
rcourtier Sep 11, 2024
2e7671f
Create a common OperationalLimitsGroup for OperationalLimitSet having…
rcourtier Sep 12, 2024
70f6a11
Fix CGMES export to avoid duplicate OperationalLimitType and map 1 CG…
rcourtier Sep 12, 2024
fac231f
Reworked setting of selected group to avoid changing IIDM API
rcourtier Sep 16, 2024
6c1779c
Add a parameter to export all OperationalLimitsGroup or only the sele…
rcourtier Sep 16, 2024
3045afa
Reworked to comply with quality gate
rcourtier Sep 16, 2024
9916f5e
Add documentation (CGMES import and export and IIDM modeling)
rcourtier Sep 17, 2024
faf6b05
Merge branch 'main' into cgmes_import_handle_multiple_limitsgroups
zamarrenolm Sep 18, 2024
20a4dfa
Map the CGMES OperationalLimitSet.rdfid to the IIDM OperationalLimits…
rcourtier Sep 19, 2024
1be3e9e
Support CGMES import and export of OperationalLimitSet.rdfid and name…
rcourtier Sep 20, 2024
1d1b34d
Merge branch 'main' into cgmes_import_handle_multiple_limitsgroups
zamarrenolm Sep 26, 2024
041d5fe
Review the LoadingLimitsMapping class: map the LoadingLimitsAdders wi…
rcourtier Sep 27, 2024
7046313
Using OperationalLimitSet mRID instead of rdf:ID.
rcourtier Sep 27, 2024
790824f
Merge branch 'main' into cgmes_import_handle_multiple_limitsgroups
olperr1 Sep 30, 2024
c141a48
Simplify the addAll method in LoadingLimitsMapping
rcourtier Sep 30, 2024
6684c68
Integrate the limits validity check in a filter of the stream
rcourtier Sep 30, 2024
2adbd5d
Merge branch 'main' into cgmes_import_handle_multiple_limitsgroups
zamarrenolm Sep 30, 2024
6b6a125
Keep the 3w-transformer stream instead of casting it to a set
rcourtier Sep 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2205,10 +2205,6 @@
<cim:IdentifiedObject.name>_OLS_V_1000</cim:IdentifiedObject.name>
<cim:OperationalLimitSet.Equipment rdf:resource="#_64901aec-5a8a-4bcb-8ca7-a3ddbfcd0e6c" />
</cim:OperationalLimitSet>
<cim:OperationalLimitSet rdf:ID="new_limit_set_2">
<cim:IdentifiedObject.name>_OLS_V_1000</cim:IdentifiedObject.name>
<cim:OperationalLimitSet.Terminal rdf:resource="#_4c19ace6-c825-4c5b-87d9-031e6e6a3379" />
</cim:OperationalLimitSet>
<cim:OperationalLimitSet rdf:ID="new_limit_set_3">
<cim:IdentifiedObject.name>_OLS_V_1000</cim:IdentifiedObject.name>
<cim:OperationalLimitSet.Equipment rdf:resource="#_84ed55f4-61f5-4d9d-8755-bba7b877a246" />
Expand Down Expand Up @@ -2261,7 +2257,7 @@
</cim:VoltageLimit>
<cim:ApparentPowerLimit rdf:ID="new_apparent_power_limit_1">
<cim:IdentifiedObject.name>patl_apparent_power</cim:IdentifiedObject.name>
<cim:OperationalLimit.OperationalLimitSet rdf:resource="#new_limit_set_2" />
<cim:OperationalLimit.OperationalLimitSet rdf:resource="#_88aa13e4-d3fe-4c47-9e47-39f8bb805e73" />
<cim:OperationalLimit.OperationalLimitType rdf:resource="#patl" />
<cim:ApparentPowerLimit.value>22863.1</cim:ApparentPowerLimit.value>
</cim:ApparentPowerLimit>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ private void addParametersToContext(CgmesExportContext context, Properties param
.setExportFlowsForSwitches(Parameter.readBoolean(getFormat(), params, EXPORT_POWER_FLOWS_FOR_SWITCHES_PARAMETER, defaultValueConfig))
.setExportTransformersWithHighestVoltageAtEnd1(Parameter.readBoolean(getFormat(), params, EXPORT_TRANSFORMERS_WITH_HIGHEST_VOLTAGE_AT_END1_PARAMETER, defaultValueConfig))
.setExportLoadFlowStatus(Parameter.readBoolean(getFormat(), params, EXPORT_LOAD_FLOW_STATUS_PARAMETER, defaultValueConfig))
.setExportAllLimitsGroup(Parameter.readBoolean(getFormat(), params, EXPORT_ALL_LIMITS_GROUP_PARAMETER, defaultValueConfig))
.setMaxPMismatchConverged(Parameter.readDouble(getFormat(), params, MAX_P_MISMATCH_CONVERGED_PARAMETER, defaultValueConfig))
.setMaxQMismatchConverged(Parameter.readDouble(getFormat(), params, MAX_Q_MISMATCH_CONVERGED_PARAMETER, defaultValueConfig))
.setExportSvInjectionsForSlacks(Parameter.readBoolean(getFormat(), params, EXPORT_SV_INJECTIONS_FOR_SLACKS_PARAMETER, defaultValueConfig))
Expand Down Expand Up @@ -542,6 +543,7 @@ public String getFormat() {
public static final String MODEL_DESCRIPTION = "iidm.export.cgmes.model-description";
public static final String EXPORT_TRANSFORMERS_WITH_HIGHEST_VOLTAGE_AT_END1 = "iidm.export.cgmes.export-transformers-with-highest-voltage-at-end1";
public static final String EXPORT_LOAD_FLOW_STATUS = "iidm.export.cgmes.export-load-flow-status";
public static final String EXPORT_ALL_LIMITS_GROUP = "iidm.export.cgmes.export-all-limits-group";
public static final String MAX_P_MISMATCH_CONVERGED = "iidm.export.cgmes.max-p-mismatch-converged";
public static final String MAX_Q_MISMATCH_CONVERGED = "iidm.export.cgmes.max-q-mismatch-converged";
public static final String EXPORT_SV_INJECTIONS_FOR_SLACKS = "iidm.export.cgmes.export-sv-injections-for-slacks";
Expand Down Expand Up @@ -631,6 +633,11 @@ public String getFormat() {
ParameterType.BOOLEAN,
"Export load flow status of topological islands",
CgmesExportContext.EXPORT_LOAD_FLOW_STATUS_DEFAULT_VALUE);
private static final Parameter EXPORT_ALL_LIMITS_GROUP_PARAMETER = new Parameter(
EXPORT_ALL_LIMITS_GROUP,
ParameterType.BOOLEAN,
"True to export all OperationalLimitsGroup, False to export only the selected group",
CgmesExportContext.EXPORT_LOAD_FLOW_STATUS_DEFAULT_VALUE);
private static final Parameter MAX_P_MISMATCH_CONVERGED_PARAMETER = new Parameter(
MAX_P_MISMATCH_CONVERGED,
ParameterType.DOUBLE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ public Network convert(ReportNode reportNode) {

convert(cgmes.operationalLimits(), CgmesNames.OPERATIONAL_LIMIT, context);
context.loadingLimitsMapping().addAll();
setSelectedOperationalLimitsGroup(context);

if (config.convertSvInjections()) {
convert(cgmes.svInjections(), CgmesNames.SV_INJECTION, context);
Expand Down Expand Up @@ -278,6 +279,45 @@ public Network convert(ReportNode reportNode) {
return network;
}

/**
* Retrieve the Collection of OperationalLimitGroups for identifiable that have flow limits
* (branch, dangling line, 3w-transformer).
* If the collection has only one element, it gets to be the identifiable's selectedGroup.
* If there is more than one element in the collection, don't set any as selected.
* @param context The conversion's Context.
*/
private void setSelectedOperationalLimitsGroup(Context context) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that to avoid all these loops we could do the selection in LoadingLimitsMapping::addAll.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difficulty is that we should loop over all FlowsLimitsHolder to set the selected set, but there is no easy link to go from OperationalLimitsGroup which we have in LoadingLimitsMapping to FlowsLimitsHolder.

// Set selected limits group for branches
context.network().getBranchStream().map(b -> (Branch<?>) b).forEach(branch -> {
// Side 1
Collection<OperationalLimitsGroup> limitsHolder1 = branch.getOperationalLimitsGroups1();
if (limitsHolder1.size() == 1) {
branch.setSelectedOperationalLimitsGroup1(limitsHolder1.iterator().next().getId());
}
// Side 2
Collection<OperationalLimitsGroup> limitsHolder2 = branch.getOperationalLimitsGroups2();
if (limitsHolder2.size() == 1) {
branch.setSelectedOperationalLimitsGroup2(limitsHolder2.iterator().next().getId());
}
});

// Set selected limits group for Dangling lines
context.network().getDanglingLineStream().forEach(dl -> {
Collection<OperationalLimitsGroup> limitsHolder = dl.getOperationalLimitsGroups();
if (limitsHolder.size() == 1) {
dl.setSelectedOperationalLimitsGroup(limitsHolder.iterator().next().getId());
}
});

// Set selected limits group for 3w transformers legs
context.network().getThreeWindingsTransformerStream().flatMap(ThreeWindingsTransformer::getLegStream).forEach(leg -> {
Collection<OperationalLimitsGroup> limitsHolder = leg.getOperationalLimitsGroups();
if (limitsHolder.size() == 1) {
leg.setSelectedOperationalLimitsGroup(limitsHolder.iterator().next().getId());
}
});
}

private void handleDangingLineDisconnectedAtBoundary(Network network, Context context) {
if (config.disconnectNetworkSideOfDanglingLinesIfBoundaryIsDisconnected()) {
for (DanglingLine dl : network.getDanglingLines()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,58 @@
*/
package com.powsybl.cgmes.conversion;

import com.powsybl.iidm.network.LoadingLimitsAdder;
import com.powsybl.cgmes.model.CgmesNames;
import com.powsybl.iidm.network.*;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.*;
import java.util.stream.Stream;

/**
* @author Miora Ralambotiana {@literal <miora.ralambotiana at rte-france.com>}
* @author Romain Courtier {@literal <romain.courtier at rte-france.com>}
*/
public class LoadingLimitsMapping {

protected final Map<String, LoadingLimitsAdder<?, ?>> adders = new HashMap<>();
private final Context context;
private final Map<OperationalLimitsGroup, CurrentLimitsAdder> currentLimitsAdders;
private final Map<OperationalLimitsGroup, ActivePowerLimitsAdder> activePowerLimitsAdders;
private final Map<OperationalLimitsGroup, ApparentPowerLimitsAdder> apparentPowerLimitsAdders;

LoadingLimitsMapping(Context context) {
this.context = Objects.requireNonNull(context);
this.currentLimitsAdders = new HashMap<>();
this.activePowerLimitsAdders = new HashMap<>();
this.apparentPowerLimitsAdders = new HashMap<>();
}

public LoadingLimitsAdder computeIfAbsentLoadingLimitsAdder(String id, Supplier<LoadingLimitsAdder<?, ?>> supplier) {
return adders.computeIfAbsent(id, s -> supplier.get());
/**
* Get or create the limit adder for the given limits group and limit subclass.
* Different CGMES OperationalLimit can be mapped to the same IIDM LoadingLimit,
* hence the need to retrieve the limit adder if it has already been created before.
* @param limitsGroup The OperationalLimitsGroup to which the LoadingLimitsAdder shall add the limits.
* @param limitSubClass The operational limit subclass indicating which LoadingLimitsAdder subclass shall be used.
* @return The LoadingLimitsAdder for the given limits group and limit subclass.
*/
public LoadingLimitsAdder getLoadingLimitsAdder(OperationalLimitsGroup limitsGroup, String limitSubClass) {
return switch (limitSubClass) {
case CgmesNames.CURRENT_LIMIT -> currentLimitsAdders.computeIfAbsent(limitsGroup, s -> limitsGroup.newCurrentLimits());
case CgmesNames.ACTIVE_POWER_LIMIT -> activePowerLimitsAdders.computeIfAbsent(limitsGroup, s -> limitsGroup.newActivePowerLimits());
case CgmesNames.APPARENT_POWER_LIMIT -> apparentPowerLimitsAdders.computeIfAbsent(limitsGroup, s -> limitsGroup.newApparentPowerLimits());
default -> throw new IllegalArgumentException();
};
}

/**
* Execute the add method for all the valid LoadingLimitsAdder stored in this mapping class.
* This method shall be called after all the CGMES OperationalLimit have been converted.
*/
void addAll() {
for (Map.Entry<String, LoadingLimitsAdder<?, ?>> entry : adders.entrySet()) {
if (!Double.isNaN(entry.getValue().getPermanentLimit()) || entry.getValue().hasTemporaryLimits()) {
entry.getValue()
.fixLimits(context.config().getMissingPermanentLimitPercentage(), context::fixed)
.add();
}
}
adders.clear();
Stream.of(currentLimitsAdders, activePowerLimitsAdders, apparentPowerLimitsAdders)
.flatMap(m -> m.values().stream())
.filter(adder -> !Double.isNaN(adder.getPermanentLimit()) || adder.hasTemporaryLimits())
.forEach(adder -> adder.fixLimits(context.config().getMissingPermanentLimitPercentage(), context::fixed)
.add());

Stream.of(currentLimitsAdders, activePowerLimitsAdders, apparentPowerLimitsAdders).forEach(Map::clear);
}
}
Loading