From 0980ac0382360f7eea75851f28282e6bf039c345 Mon Sep 17 00:00:00 2001 From: Thomas Bouquet <63302082+bqth29@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:14:33 +0200 Subject: [PATCH] [CSA] [Import] Reorder usage rules definition (#972) * Add usage rules by constraining order Signed-off-by: Thomas Bouquet * Unit tests for usage method helpers Signed-off-by: Thomas Bouquet * Remove useless field in query Signed-off-by: Thomas Bouquet * Add comprehensive tests with CSA profiles Signed-off-by: Thomas Bouquet * Update after comments Signed-off-by: Thomas Bouquet --------- Signed-off-by: Thomas Bouquet Signed-off-by: belthlemar --- .../craccreator/CsaProfileCracCreator.java | 18 +- .../craccreator/CsaProfileCracUtils.java | 4 +- .../cnec/CsaProfileCnecCreator.java | 8 +- .../CsaProfileContingencyCreator.java | 2 +- .../remedialaction/AssociationStatus.java | 17 + .../CsaProfileRemedialActionsCreator.java | 237 +++++--------- .../ElementaryActionsHelper.java | 22 +- .../remedialaction/NetworkActionCreator.java | 6 +- .../OnConstraintUsageRuleHelper.java | 151 +++++---- .../OnContingencyStateUsageRuleHelper.java | 64 ++++ .../remedialaction/PstRangeActionCreator.java | 2 +- .../src/main/resources/csa_profile.sparql | 3 +- .../CsaProfileCracCreationTestUtil.java | 4 + .../craccreator/CsaProfileCracUtilsTest.java | 2 +- .../cnec/AngleCnecCreationTest.java | 2 +- .../cnec/FlowCnecCreationTest.java | 2 +- .../cnec/VoltageCnecCreationTest.java | 4 +- .../AutoRemedialActionTest.java | 5 +- .../GroupRemedialActionTest.java | 2 +- .../OnConstraintUsageRuleHelperTest.java | 305 ++++++++++++++++++ ...OnContingencyStateUsageRuleHelperTest.java | 109 +++++++ .../RemedialActionCreationTest.java | 80 ++++- .../remedialactions/AutoRemedialActions.zip | Bin 327034 -> 34786 bytes .../profiles/remedialactions/UsageRules.zip | Bin 0 -> 27768 bytes 24 files changed, 784 insertions(+), 265 deletions(-) create mode 100644 data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/AssociationStatus.java create mode 100644 data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnContingencyStateUsageRuleHelper.java create mode 100644 data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnConstraintUsageRuleHelperTest.java create mode 100644 data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnContingencyStateUsageRuleHelperTest.java create mode 100644 data/crac-creation/crac-creator-csa-profiles/src/test/resources/profiles/remedialactions/UsageRules.zip diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracCreator.java b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracCreator.java index 6c5090d346..a521f705b8 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracCreator.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracCreator.java @@ -18,7 +18,6 @@ import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.contingency.CsaProfileContingencyCreator; import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.remedialaction.CsaProfileRemedialActionsCreator; import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.remedialaction.ElementaryActionsHelper; -import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.remedialaction.OnConstraintUsageRuleHelper; import com.google.auto.service.AutoService; import com.powsybl.iidm.network.Network; import com.powsybl.openrao.data.craccreation.creator.csaprofile.parameters.CsaCracCreationParameters; @@ -61,12 +60,12 @@ public CsaProfileCracCreationContext createCrac(CsaProfileCrac nativeCrac, Netwo Map overridingData = nativeCrac.getOverridingCracData(offsetDateTime); PropertyBags contingencies = CsaProfileCracUtils.overrideData(nativeCrac.getContingencies(), overridingData, CsaProfileConstants.OverridingObjectsFields.CONTINGENCY); PropertyBags assessedElements = CsaProfileCracUtils.overrideData(nativeCrac.getAssessedElements(), overridingData, CsaProfileConstants.OverridingObjectsFields.ASSESSED_ELEMENT); - PropertyBags assessedElementsWithContingencies = CsaProfileCracUtils.overrideData(nativeCrac.getAssessedElementsWithContingencies(), overridingData, CsaProfileConstants.OverridingObjectsFields.ASSESSED_ELEMENT_WITH_CONTINGENCY); + PropertyBags assessedElementWithContingencies = CsaProfileCracUtils.overrideData(nativeCrac.getAssessedElementsWithContingencies(), overridingData, CsaProfileConstants.OverridingObjectsFields.ASSESSED_ELEMENT_WITH_CONTINGENCY); PropertyBags currentLimits = CsaProfileCracUtils.overrideData(nativeCrac.getCurrentLimits(), overridingData, CsaProfileConstants.OverridingObjectsFields.CURRENT_LIMIT); PropertyBags voltageLimits = CsaProfileCracUtils.overrideData(nativeCrac.getVoltageLimits(), overridingData, CsaProfileConstants.OverridingObjectsFields.VOLTAGE_LIMIT); PropertyBags angleLimits = CsaProfileCracUtils.overrideData(nativeCrac.getAngleLimits(), overridingData, CsaProfileConstants.OverridingObjectsFields.VOLTAGE_ANGLE_LIMIT); - PropertyBags assessedElementsWithRemedialAction = CsaProfileCracUtils.overrideData(nativeCrac.getAssessedElementsWithRemedialAction(), overridingData, CsaProfileConstants.OverridingObjectsFields.ASSESSED_ELEMENT_WITH_REMEDIAL_ACTION); - PropertyBags contingenciesWithRemedialAction = CsaProfileCracUtils.overrideData(nativeCrac.getContingencyWithRemedialAction(), overridingData, CsaProfileConstants.OverridingObjectsFields.CONTINGENCY_WITH_REMEDIAL_ACTION); + PropertyBags assessedElementWithRemedialActions = CsaProfileCracUtils.overrideData(nativeCrac.getAssessedElementsWithRemedialAction(), overridingData, CsaProfileConstants.OverridingObjectsFields.ASSESSED_ELEMENT_WITH_REMEDIAL_ACTION); + PropertyBags contingencyWithRemedialActions = CsaProfileCracUtils.overrideData(nativeCrac.getContingencyWithRemedialAction(), overridingData, CsaProfileConstants.OverridingObjectsFields.CONTINGENCY_WITH_REMEDIAL_ACTION); PropertyBags gridStateAlterationRemedialAction = CsaProfileCracUtils.overrideData(nativeCrac.getGridStateAlterationRemedialAction(), overridingData, CsaProfileConstants.OverridingObjectsFields.GRID_STATE_ALTERATION_REMEDIAL_ACTION); PropertyBags remedialActionSchemes = CsaProfileCracUtils.overrideData(nativeCrac.getRemedialActionScheme(), overridingData, CsaProfileConstants.OverridingObjectsFields.REMEDIAL_ACTION_SCHEME); PropertyBags gridStateAlterationsCollection = CsaProfileCracUtils.overrideData(nativeCrac.getGridStateAlterationCollection(), overridingData, CsaProfileConstants.OverridingObjectsFields.GRID_STATE_ALTERATION); @@ -80,11 +79,10 @@ public CsaProfileCracCreationContext createCrac(CsaProfileCrac nativeCrac, Netwo createContingencies(contingencies, nativeCrac.getContingencyEquipments()); - createCnecs(assessedElements, assessedElementsWithContingencies, currentLimits, voltageLimits, angleLimits, cracCreationParameters.getDefaultMonitoredSides(), regionEicCode); + createCnecs(assessedElements, assessedElementWithContingencies, currentLimits, voltageLimits, angleLimits, cracCreationParameters.getDefaultMonitoredSides(), regionEicCode); - OnConstraintUsageRuleHelper onConstraintUsageRuleAdder = new OnConstraintUsageRuleHelper(creationContext.getCnecCreationContexts(), assessedElements, assessedElementsWithRemedialAction); - ElementaryActionsHelper elementaryActionsHelper = new ElementaryActionsHelper(gridStateAlterationRemedialAction, schemeRemedialActions, remedialActionSchemes, nativeCrac.getStage(), gridStateAlterationsCollection, assessedElementsWithRemedialAction, contingenciesWithRemedialAction, staticPropertyRanges, topologyActions, rotatingMachineActions, shuntCompensatorModifications, tapPositionActions, nativeCrac.getRemedialActionGroups(), remedialActionDependencies); - createRemedialActions(onConstraintUsageRuleAdder, elementaryActionsHelper, spsMaxTimeToImplementThreshold); + ElementaryActionsHelper elementaryActionsHelper = new ElementaryActionsHelper(gridStateAlterationRemedialAction, schemeRemedialActions, remedialActionSchemes, nativeCrac.getStage(), gridStateAlterationsCollection, assessedElementWithRemedialActions, contingencyWithRemedialActions, staticPropertyRanges, topologyActions, rotatingMachineActions, shuntCompensatorModifications, tapPositionActions, nativeCrac.getRemedialActionGroups(), remedialActionDependencies); + createRemedialActions(elementaryActionsHelper, spsMaxTimeToImplementThreshold, assessedElements, assessedElementWithRemedialActions, contingencyWithRemedialActions, creationContext.getCnecCreationContexts()); creationContext.buildCreationReport(); return creationContext.creationSuccess(crac); } @@ -116,8 +114,8 @@ private boolean checkTimeCoherence(PropertyBag header, OffsetDateTime offsetDate return isValidInterval(offsetDateTime, startTime, endTime); } - private void createRemedialActions(OnConstraintUsageRuleHelper onConstraintUsageRuleAdder, ElementaryActionsHelper elementaryActionsHelper, int spsMaxTimeToImplementThreshold) { - new CsaProfileRemedialActionsCreator(crac, network, creationContext, onConstraintUsageRuleAdder, elementaryActionsHelper, spsMaxTimeToImplementThreshold); + private void createRemedialActions(ElementaryActionsHelper elementaryActionsHelper, int spsMaxTimeToImplementThreshold, PropertyBags assessedElements, PropertyBags assessedElementWithRemedialActions, PropertyBags contingencyWithRemedialActions, Set cnecCreationContexts) { + new CsaProfileRemedialActionsCreator(crac, network, creationContext, elementaryActionsHelper, spsMaxTimeToImplementThreshold, assessedElements, assessedElementWithRemedialActions, contingencyWithRemedialActions, cnecCreationContexts); } private void createContingencies(PropertyBags contingenciesPropertyBags, PropertyBags contingencyEquipmentsPropertyBags) { diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracUtils.java b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracUtils.java index 08ded4c74c..c95790f229 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracUtils.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracUtils.java @@ -30,10 +30,10 @@ private CsaProfileCracUtils() { } - public static Map> getMappedPropertyBagsSet(PropertyBags propertyBags, String property) { + public static Map> groupPropertyBags(PropertyBags propertyBags, String groupingPoperty) { Map> mappedPropertyBags = new HashMap<>(); for (PropertyBag propertyBag : propertyBags) { - String propValue = propertyBag.getId(property); + String propValue = propertyBag.getId(groupingPoperty); Set propPropertyBags = mappedPropertyBags.computeIfAbsent(propValue, k -> new HashSet<>()); propPropertyBags.add(propertyBag); } diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/CsaProfileCnecCreator.java b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/CsaProfileCnecCreator.java index e92a340ba5..7c790ac6c0 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/CsaProfileCnecCreator.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/CsaProfileCnecCreator.java @@ -42,10 +42,10 @@ public CsaProfileCnecCreator(Crac crac, Network network, PropertyBags assessedEl this.crac = crac; this.network = network; this.assessedElementsPropertyBags = assessedElementsPropertyBags; - this.assessedElementsWithContingenciesPropertyBags = CsaProfileCracUtils.getMappedPropertyBagsSet(assessedElementsWithContingenciesPropertyBags, CsaProfileConstants.REQUEST_ASSESSED_ELEMENT); - this.currentLimitsPropertyBags = CsaProfileCracUtils.getMappedPropertyBagsSet(currentLimitsPropertyBags, CsaProfileConstants.REQUEST_CURRENT_LIMIT); - this.voltageLimitsPropertyBags = CsaProfileCracUtils.getMappedPropertyBagsSet(voltageLimitsPropertyBags, CsaProfileConstants.REQUEST_VOLTAGE_LIMIT); - this.angleLimitsPropertyBags = CsaProfileCracUtils.getMappedPropertyBagsSet(angleLimitsPropertyBags, CsaProfileConstants.REQUEST_VOLTAGE_ANGLE_LIMIT); + this.assessedElementsWithContingenciesPropertyBags = CsaProfileCracUtils.groupPropertyBags(assessedElementsWithContingenciesPropertyBags, CsaProfileConstants.REQUEST_ASSESSED_ELEMENT); + this.currentLimitsPropertyBags = CsaProfileCracUtils.groupPropertyBags(currentLimitsPropertyBags, CsaProfileConstants.REQUEST_CURRENT_LIMIT); + this.voltageLimitsPropertyBags = CsaProfileCracUtils.groupPropertyBags(voltageLimitsPropertyBags, CsaProfileConstants.REQUEST_VOLTAGE_LIMIT); + this.angleLimitsPropertyBags = CsaProfileCracUtils.groupPropertyBags(angleLimitsPropertyBags, CsaProfileConstants.REQUEST_VOLTAGE_ANGLE_LIMIT); this.cracCreationContext = cracCreationContext; this.defaultMonitoredSides = defaultMonitoredSides; this.regionEic = regionEic; diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/contingency/CsaProfileContingencyCreator.java b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/contingency/CsaProfileContingencyCreator.java index 9840e5d8a0..f21c220bfb 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/contingency/CsaProfileContingencyCreator.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/contingency/CsaProfileContingencyCreator.java @@ -52,7 +52,7 @@ public CsaProfileContingencyCreator(Crac crac, Network network, PropertyBags con this.crac = crac; this.network = network; this.contingenciesPropertyBags = contingenciesPropertyBags; - this.contingencyEquipmentsPropertyBags = CsaProfileCracUtils.getMappedPropertyBagsSet(contingencyEquipmentsPropertyBags, CsaProfileConstants.REQUEST_CONTINGENCY); + this.contingencyEquipmentsPropertyBags = CsaProfileCracUtils.groupPropertyBags(contingencyEquipmentsPropertyBags, CsaProfileConstants.REQUEST_CONTINGENCY); this.cracCreationContext = cracCreationContext; this.createAndAddContingencies(); } diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/AssociationStatus.java b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/AssociationStatus.java new file mode 100644 index 0000000000..d7d8d412ea --- /dev/null +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/AssociationStatus.java @@ -0,0 +1,17 @@ +/* + * 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/. + */ +package com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.remedialaction; + +import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileConstants; + +/** + * @author Thomas Bouquet + */ +public record AssociationStatus(boolean isValid, + CsaProfileConstants.ElementCombinationConstraintKind elementCombinationConstraintKind, + String statusDetails) { +} diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/CsaProfileRemedialActionsCreator.java b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/CsaProfileRemedialActionsCreator.java index 458315f55e..c0e4af4c6f 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/CsaProfileRemedialActionsCreator.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/CsaProfileRemedialActionsCreator.java @@ -6,14 +6,11 @@ */ package com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.remedialaction; -import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.data.cracapi.*; -import com.powsybl.openrao.data.cracapi.cnec.AngleCnec; import com.powsybl.openrao.data.cracapi.cnec.Cnec; import com.powsybl.openrao.data.cracapi.cnec.FlowCnec; import com.powsybl.openrao.data.cracapi.cnec.VoltageCnec; import com.powsybl.openrao.data.cracapi.networkaction.*; -import com.powsybl.openrao.data.cracapi.rangeaction.PstRangeActionAdder; import com.powsybl.openrao.data.cracapi.usagerule.*; import com.powsybl.openrao.data.craccreation.creator.api.ImportStatus; import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileConstants; @@ -23,10 +20,9 @@ import com.powsybl.openrao.data.craccreation.util.OpenRaoImportException; import com.powsybl.iidm.network.Network; import com.powsybl.triplestore.api.PropertyBag; -import org.apache.commons.lang3.tuple.Pair; +import com.powsybl.triplestore.api.PropertyBags; import java.util.*; -import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; @@ -37,20 +33,20 @@ public class CsaProfileRemedialActionsCreator { private final Crac crac; private final CsaProfileCracCreationContext cracCreationContext; Map contextByRaId = new TreeMap<>(); - private final OnConstraintUsageRuleHelper onConstraintUsageRuleHelper; private final ElementaryActionsHelper elementaryActionsHelper; private final NetworkActionCreator networkActionCreator; private final PstRangeActionCreator pstRangeActionCreator; - public CsaProfileRemedialActionsCreator(Crac crac, Network network, CsaProfileCracCreationContext cracCreationContext, OnConstraintUsageRuleHelper onConstraintUsageRuleHelper, ElementaryActionsHelper elementaryActionsHelper, int spsMaxTimeToImplementThreshold) { + public CsaProfileRemedialActionsCreator(Crac crac, Network network, CsaProfileCracCreationContext cracCreationContext, ElementaryActionsHelper elementaryActionsHelper, int spsMaxTimeToImplementThreshold, PropertyBags assessedElementPropertyBags, PropertyBags assessedElementWithRemedialActions, PropertyBags contingencyWithRemedialActions, Set cnecCreationContexts) { this.crac = crac; this.cracCreationContext = cracCreationContext; - this.onConstraintUsageRuleHelper = onConstraintUsageRuleHelper; this.elementaryActionsHelper = elementaryActionsHelper; this.networkActionCreator = new NetworkActionCreator(this.crac, network); this.pstRangeActionCreator = new PstRangeActionCreator(this.crac, network); - createRemedialActions(false, spsMaxTimeToImplementThreshold); - createRemedialActions(true, spsMaxTimeToImplementThreshold); + Map> linkedAeWithRa = CsaProfileCracUtils.groupPropertyBags(assessedElementWithRemedialActions, CsaProfileConstants.REQUEST_REMEDIAL_ACTION); + Map> linkedCoWithRa = CsaProfileCracUtils.groupPropertyBags(contingencyWithRemedialActions, CsaProfileConstants.REQUEST_REMEDIAL_ACTION); + createRemedialActions(false, spsMaxTimeToImplementThreshold, assessedElementPropertyBags, linkedAeWithRa, linkedCoWithRa, cnecCreationContexts); + createRemedialActions(true, spsMaxTimeToImplementThreshold, assessedElementPropertyBags, linkedAeWithRa, linkedCoWithRa, cnecCreationContexts); // standaloneRaIdsImplicatedIntoAGroup contain ids of Ra's depending on a group whether the group is imported or not Set standaloneRaIdsImplicatedIntoAGroup = createRemedialActionGroups(); standaloneRaIdsImplicatedIntoAGroup.forEach(crac::removeRemedialAction); @@ -58,7 +54,7 @@ public CsaProfileRemedialActionsCreator(Crac crac, Network network, CsaProfileCr this.cracCreationContext.setRemedialActionCreationContexts(new HashSet<>(contextByRaId.values())); } - private void createRemedialActions(boolean isSchemeRemedialAction, int spsMaxTimeToImplementThreshold) { + private void createRemedialActions(boolean isSchemeRemedialAction, int spsMaxTimeToImplementThreshold, PropertyBags assessedElementPropertyBags, Map> linkedAeWithRa, Map> linkedCoWithRa, Set cnecCreationContexts) { for (PropertyBag parentRemedialActionPropertyBag : elementaryActionsHelper.getParentRemedialActionPropertyBags(isSchemeRemedialAction)) { List alterations = new ArrayList<>(); String remedialActionId = parentRemedialActionPropertyBag.get(CsaProfileConstants.MRID); @@ -78,21 +74,15 @@ private void createRemedialActions(boolean isSchemeRemedialAction, int spsMaxTim remedialActionAdder.withOperator(CsaProfileCracUtils.getTsoNameFromUrl(tsoName)); } speedOpt.ifPresent(remedialActionAdder::withSpeed); - if (elementaryActionsHelper.getContingenciesByRemedialAction().containsKey(remedialActionId)) { - addOnContingencyStateUsageRules(parentRemedialActionPropertyBag, remedialActionId, elementaryActionsHelper.getContingenciesByRemedialAction(), remedialActionAdder, alterations, isSchemeRemedialAction, spsMaxTimeToImplementThreshold, remedialActionType); - } else { - if (!isSchemeRemedialAction) { - // no contingency linked to RA --> on instant usage rule if remedial action is not Auto - addOnInstantUsageRules(parentRemedialActionPropertyBag, remedialActionAdder, remedialActionId, alterations, spsMaxTimeToImplementThreshold); - } else { - throw new OpenRaoImportException(ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action " + remedialActionId + " will not be imported because no contingency is linked to the remedial action"); - } - } + + Instant instant = defineInstant(isSchemeRemedialAction, parentRemedialActionPropertyBag, remedialActionId, spsMaxTimeToImplementThreshold); + addUsageRules(remedialActionId, assessedElementPropertyBags, linkedAeWithRa.getOrDefault(remedialActionId, Set.of()), linkedCoWithRa.getOrDefault(remedialActionId, Set.of()), cnecCreationContexts, remedialActionAdder, alterations, instant, isSchemeRemedialAction, remedialActionType); remedialActionAdder.add(); + if (alterations.isEmpty()) { contextByRaId.put(remedialActionId, CsaProfileElementaryCreationContext.imported(remedialActionId, remedialActionId, targetRemedialActionNameOpt.orElse(remedialActionId), "", false)); } else { - contextByRaId.put(remedialActionId, CsaProfileElementaryCreationContext.imported(remedialActionId, remedialActionId, targetRemedialActionNameOpt.orElse(remedialActionId), String.join(". ", alterations), true)); + contextByRaId.put(remedialActionId, CsaProfileElementaryCreationContext.imported(remedialActionId, remedialActionId, targetRemedialActionNameOpt.orElse(remedialActionId), String.join(" ", alterations), true)); } } catch (OpenRaoImportException e) { @@ -101,50 +91,84 @@ private void createRemedialActions(boolean isSchemeRemedialAction, int spsMaxTim } } - private RemedialActionAdder getRemedialActionAdder(String remedialActionId, String elementaryActionsAggregatorId, RemedialActionType remedialActionType, boolean isSchemeRemedialAction, List alterations) { - RemedialActionAdder remedialActionAdder; - if (remedialActionType.equals(RemedialActionType.NETWORK_ACTION)) { - remedialActionAdder = networkActionCreator.getNetworkActionAdder(elementaryActionsHelper.getTopologyActions(isSchemeRemedialAction), elementaryActionsHelper.getRotatingMachineActions(isSchemeRemedialAction), elementaryActionsHelper.getShuntCompensatorModifications(isSchemeRemedialAction), elementaryActionsHelper.getStaticPropertyRangesByElementaryActionsAggregator(), remedialActionId, elementaryActionsAggregatorId, alterations); - } else { - remedialActionAdder = pstRangeActionCreator.getPstRangeActionAdder(elementaryActionsHelper.getTapPositionActions(isSchemeRemedialAction), elementaryActionsHelper.getStaticPropertyRangesByElementaryActionsAggregator(), remedialActionId, elementaryActionsAggregatorId, alterations); + private void addUsageRules(String remedialActionId, PropertyBags assessedElementPropertyBags, Set linkedAssessedElementWithRemedialActions, Set linkedContingencyWithRemedialActions, Set cnecCreationContexts, RemedialActionAdder remedialActionAdder, List alterations, Instant instant, boolean isSchemeRemedialAction, RemedialActionType remedialActionType) { + if (addOnConstraintUsageRules(remedialActionId, assessedElementPropertyBags, linkedAssessedElementWithRemedialActions, linkedContingencyWithRemedialActions, cnecCreationContexts, remedialActionAdder, alterations, instant, isSchemeRemedialAction, remedialActionType)) { + return; } - return remedialActionAdder; + if (addOnContingencyStateUsageRules(remedialActionId, linkedContingencyWithRemedialActions, remedialActionAdder, alterations, instant, isSchemeRemedialAction, remedialActionType)) { + return; + } + addOnInstantUsageRules(remedialActionId, remedialActionAdder, instant); } - private void addOnInstantUsageRules(PropertyBag parentRemedialActionPropertyBag, RemedialActionAdder remedialActionAdder, String remedialActionId, List alterations, int durationLimit) { - Instant instant = parentRemedialActionPropertyBag.get(CsaProfileConstants.KIND).equals(CsaProfileConstants.RemedialActionKind.PREVENTIVE.toString()) ? crac.getPreventiveInstant() : crac.getInstant(InstantKind.CURATIVE); - if (instant.isCurative()) { - instant = defineInstant(false, parentRemedialActionPropertyBag, remedialActionId, durationLimit); + private boolean addOnConstraintUsageRules(String remedialActionId, PropertyBags assessedElementPropertyBags, Set linkedAssessedElementWithRemedialActions, Set linkedContingencyWithRemedialActions, Set cnecCreationContexts, RemedialActionAdder remedialActionAdder, List alterations, Instant instant, boolean isSchemeRemedialAction, RemedialActionType remedialActionType) { + Map cnecStatusMap = OnConstraintUsageRuleHelper.processCnecsLinkedToRemedialAction(crac, remedialActionId, assessedElementPropertyBags, linkedAssessedElementWithRemedialActions, linkedContingencyWithRemedialActions, cnecCreationContexts); + cnecStatusMap.forEach((cnecId, cnecStatus) -> { + if (cnecStatus.isValid()) { + Cnec cnec = crac.getCnec(cnecId); + UsageMethod usageMethod = getUsageMethod(cnecStatus.elementCombinationConstraintKind(), isSchemeRemedialAction, instant, remedialActionType); + if (isOnConstraintInstantCoherent(cnec.getState().getInstant(), instant)) { + if (cnec instanceof FlowCnec) { + remedialActionAdder.newOnFlowConstraintUsageRule() + .withInstant(instant.getId()) + .withFlowCnec(cnecId) + .withUsageMethod(usageMethod) + .add(); + } else if (cnec instanceof VoltageCnec) { + remedialActionAdder.newOnVoltageConstraintUsageRule() + .withInstant(instant.getId()) + .withVoltageCnec(cnecId) + .withUsageMethod(usageMethod) + .add(); + } else { + remedialActionAdder.newOnAngleConstraintUsageRule() + .withInstant(instant.getId()) + .withAngleCnec(cnecId) + .withUsageMethod(usageMethod) + .add(); + } + } + } else { + alterations.add(cnecStatus.statusDetails()); + } + }); + return !linkedAssessedElementWithRemedialActions.isEmpty(); + } + + private boolean addOnContingencyStateUsageRules(String remedialActionId, Set linkedContingencyWithRemedialActions, RemedialActionAdder remedialActionAdder, List alterations, Instant instant, boolean isSchemeRemedialAction, RemedialActionType remedialActionType) { + Map contingencyStatusMap = OnContingencyStateUsageRuleHelper.processContingenciesLinkedToRemedialAction(crac, remedialActionId, linkedContingencyWithRemedialActions); + if (instant.isPreventive() && !linkedContingencyWithRemedialActions.isEmpty()) { + throw new OpenRaoImportException(ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action %s will not be imported because it is linked to a contingency but is not curative".formatted(remedialActionId)); } - boolean isLinkedToAssessedElements = elementaryActionsHelper.remedialActionIsLinkedToAssessedElements(remedialActionId); + contingencyStatusMap.forEach((contingencyId, contingencyStatus) -> { + if (contingencyStatus.isValid()) { + remedialActionAdder.newOnContingencyStateUsageRule() + .withInstant(instant.getId()) + .withContingency(contingencyId) + .withUsageMethod(getUsageMethod(contingencyStatus.elementCombinationConstraintKind(), isSchemeRemedialAction, instant, remedialActionType)) + .add(); + } else { + alterations.add(contingencyStatus.statusDetails()); + } + }); + return !linkedContingencyWithRemedialActions.isEmpty(); + } - addOnConstraintUsageRules(instant, remedialActionAdder, remedialActionId, alterations); - if (!isLinkedToAssessedElements) { - remedialActionAdder.newOnInstantUsageRule().withUsageMethod(UsageMethod.AVAILABLE).withInstant(instant.getId()).add(); + private void addOnInstantUsageRules(String remedialActionId, RemedialActionAdder remedialActionAdder, Instant instant) { + if (instant.isAuto()) { + throw new OpenRaoImportException(ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action %s will not be imported because no contingency or assessed element is linked to the remedial action and this is nor supported for ARAs".formatted(remedialActionId)); } + remedialActionAdder.newOnInstantUsageRule().withInstant(instant.getId()).withUsageMethod(UsageMethod.AVAILABLE).add(); } - private void checkElementCombinationConstraintKindsCoherence(String remedialActionId, Map> linkedContingencyWithRAs, List alterations, boolean isSchemeRemedialAction) { - // The same contingency cannot have different Element Combination Constraint Kinds - // The same contingency can appear several times, so we need to create a memory system - Set contingenciesWithIncluded = new HashSet<>(); - Set contingenciesWithConsidered = new HashSet<>(); - Set linkedContingencyWithRA = linkedContingencyWithRAs.get(remedialActionId); - for (PropertyBag propertyBag : linkedContingencyWithRA) { - String combinationKind = propertyBag.get(CsaProfileConstants.COMBINATION_CONSTRAINT_KIND); - String contingencyId = propertyBag.getId(CsaProfileConstants.REQUEST_CONTINGENCY); - if (combinationKind.equals(CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED.toString())) { - contingenciesWithIncluded.add(contingencyId); - } else if (combinationKind.equals(CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED.toString())) { - if (isSchemeRemedialAction) { - throw new OpenRaoImportException(ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action " + remedialActionId + " will not be imported because it must be linked to the contingency " + contingencyId + " with an 'included' ElementCombinationConstraintKind"); - } - contingenciesWithConsidered.add(contingencyId); - } - if (contingenciesWithIncluded.contains(contingencyId) && contingenciesWithConsidered.contains(contingencyId)) { - throw new OpenRaoImportException(ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action " + remedialActionId + " will not be imported because the ElementCombinationConstraintKinds that link the remedial action to the contingency " + contingencyId + " are different"); - } + private RemedialActionAdder getRemedialActionAdder(String remedialActionId, String elementaryActionsAggregatorId, RemedialActionType remedialActionType, boolean isSchemeRemedialAction, List alterations) { + RemedialActionAdder remedialActionAdder; + if (remedialActionType.equals(RemedialActionType.NETWORK_ACTION)) { + remedialActionAdder = networkActionCreator.getNetworkActionAdder(elementaryActionsHelper.getTopologyActions(isSchemeRemedialAction), elementaryActionsHelper.getRotatingMachineActions(isSchemeRemedialAction), elementaryActionsHelper.getShuntCompensatorModifications(isSchemeRemedialAction), elementaryActionsHelper.getStaticPropertyRangesByElementaryActionsAggregator(), remedialActionId, elementaryActionsAggregatorId, alterations); + } else { + remedialActionAdder = pstRangeActionCreator.getPstRangeActionAdder(elementaryActionsHelper.getTapPositionActions(isSchemeRemedialAction), elementaryActionsHelper.getStaticPropertyRangesByElementaryActionsAggregator(), remedialActionId, elementaryActionsAggregatorId); } + return remedialActionAdder; } private static void checkAvailability(PropertyBag remedialActionPropertyBag, String remedialActionId) { @@ -169,99 +193,14 @@ private Optional getSpeedOpt(RemedialActionType remedialActionType, Str } } - private void addOnConstraintUsageRules(Instant remedialActionInstant, RemedialActionAdder remedialActionAdder, String importableRemedialActionId, List alterations) { - UsageMethod usageMethod = remedialActionInstant.isAuto() && remedialActionAdder instanceof PstRangeActionAdder ? UsageMethod.FORCED : UsageMethod.AVAILABLE; - if (!onConstraintUsageRuleHelper.getExcludedCnecsByRemedialAction().containsKey(importableRemedialActionId)) { - onConstraintUsageRuleHelper.getImportedCnecsCombinableWithRas().forEach(addOnConstraintUsageRuleForCnec(remedialActionInstant, remedialActionAdder, usageMethod)); - } else { - alterations.add(String.format("The association 'RemedialAction'/'Cnecs' '%s'/'%s' will be ignored because 'excluded' combination constraint kind is not supported", importableRemedialActionId, String.join(". ", onConstraintUsageRuleHelper.getExcludedCnecsByRemedialAction().get(importableRemedialActionId)))); - } - if (onConstraintUsageRuleHelper.getConsideredAndIncludedCnecsByRemedialAction().containsKey(importableRemedialActionId)) { - onConstraintUsageRuleHelper.getConsideredAndIncludedCnecsByRemedialAction().get(importableRemedialActionId).forEach(addOnConstraintUsageRuleForCnec(remedialActionInstant, remedialActionAdder, usageMethod)); - } - } - - private Consumer addOnConstraintUsageRuleForCnec(Instant remedialActionInstant, RemedialActionAdder remedialActionAdder, UsageMethod usageMethod) { - return cnecId -> { - Cnec cnec = crac.getCnec(cnecId); - if (isOnConstraintInstantCoherent(cnec.getState().getInstant(), remedialActionInstant)) { - if (cnec instanceof FlowCnec) { - remedialActionAdder.newOnFlowConstraintUsageRule() - .withInstant(remedialActionInstant.getId()) - .withFlowCnec(cnecId) - .withUsageMethod(usageMethod) - .add(); - } else if (cnec instanceof VoltageCnec) { - remedialActionAdder.newOnVoltageConstraintUsageRule() - .withInstant(remedialActionInstant.getId()) - .withVoltageCnec(cnecId) - .withUsageMethod(usageMethod) - .add(); - } else if (cnec instanceof AngleCnec) { - remedialActionAdder.newOnAngleConstraintUsageRule() - .withInstant(remedialActionInstant.getId()) - .withAngleCnec(cnecId) - .withUsageMethod(usageMethod) - .add(); - } else { - throw new OpenRaoException(String.format("Unsupported cnec type %s", cnec.getClass().toString())); - } - } - }; - } - private static boolean isOnConstraintInstantCoherent(Instant cnecInstant, Instant remedialInstant) { return remedialInstant.isAuto() ? cnecInstant.isAuto() : !cnecInstant.comesBefore(remedialInstant); } - private void addOnContingencyStateUsageRules(PropertyBag parentRemedialActionPropertyBag, String remedialActionId, Map> linkedContingencyWithRAs, RemedialActionAdder remedialActionAdder, List alterations, boolean isSchemeRemedialAction, int durationLimit, RemedialActionType remedialActionType) { - checkElementCombinationConstraintKindsCoherence(remedialActionId, linkedContingencyWithRAs, alterations, isSchemeRemedialAction); - List> validContingencies = new ArrayList<>(); - List ignoredContingenciesMessages = new ArrayList<>(); - for (PropertyBag contingencyWithRemedialActionPropertyBag : linkedContingencyWithRAs.get(remedialActionId)) { - Optional normalEnabledOpt = Optional.ofNullable(contingencyWithRemedialActionPropertyBag.get(CsaProfileConstants.NORMAL_ENABLED)); - if (normalEnabledOpt.isPresent() && !Boolean.parseBoolean(normalEnabledOpt.get())) { - alterations.add(String.format("Association CO/RA '%s'/'%s' will be ignored because field 'normalEnabled' in ContingencyWithRemedialAction is set to false", contingencyWithRemedialActionPropertyBag.getId(CsaProfileConstants.REQUEST_CONTINGENCY), remedialActionId)); - } - if (!parentRemedialActionPropertyBag.get(CsaProfileConstants.KIND).equals(CsaProfileConstants.RemedialActionKind.CURATIVE.toString())) { - throw new OpenRaoImportException(ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action " + remedialActionId + " will not be imported because it is linked to a contingency but is not curative"); - } - String contingencyId = contingencyWithRemedialActionPropertyBag.getId(CsaProfileConstants.REQUEST_CONTINGENCY); - Optional normalEnabled = Optional.ofNullable(contingencyWithRemedialActionPropertyBag.get(CsaProfileConstants.NORMAL_ENABLED)); - if (normalEnabled.isPresent() && !Boolean.parseBoolean(normalEnabled.get())) { - ignoredContingenciesMessages.add("OnContingencyState usage rule for remedial action %s with contingency %s ignored because the link between the remedial action and the contingency is disabled or missing".formatted(remedialActionId, contingencyId)); - continue; - } - String combinationConstraintKind = contingencyWithRemedialActionPropertyBag.get(CsaProfileConstants.COMBINATION_CONSTRAINT_KIND); - if (!combinationConstraintKind.equals(CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED.toString()) && !combinationConstraintKind.equals(CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED.toString())) { - ignoredContingenciesMessages.add("OnContingencyState usage rule for remedial action %s with contingency %s ignored because of an illegal combinationConstraintKind".formatted(remedialActionId, contingencyId)); - continue; - } - Optional importedCsaProfileContingencyCreationContextOpt = cracCreationContext.getContingencyCreationContexts().stream().filter(co -> co.isImported() && co.getNativeId().equals(contingencyId)).findAny(); - if (importedCsaProfileContingencyCreationContextOpt.isEmpty()) { - ignoredContingenciesMessages.add("OnContingencyState usage rule for remedial action %s with contingency %s ignored because this contingency does not exist or was not imported by Open RAO".formatted(remedialActionId, contingencyId)); - continue; - } - validContingencies.add(Pair.of(importedCsaProfileContingencyCreationContextOpt.get().getElementId(), combinationConstraintKind.equals(CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED.toString()) ? CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED : CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED)); - } - - // If the remedial action is linked to an assessed element, no matter if this link or this assessed element is - // valid or not, the remedial action cannot have an onContingencyState usage rule because it would make it more - // available than what it was designed for - Instant instant = defineInstant(isSchemeRemedialAction, parentRemedialActionPropertyBag, remedialActionId, durationLimit); - addOnConstraintUsageRules(instant, remedialActionAdder, remedialActionId, alterations); - boolean isLinkedToAssessedElements = elementaryActionsHelper.remedialActionIsLinkedToAssessedElements(remedialActionId); - if (!isLinkedToAssessedElements) { - validContingencies.forEach(openRaoContingencyId -> remedialActionAdder.newOnContingencyStateUsageRule() - .withInstant(instant.getId()) - .withContingency(openRaoContingencyId.getLeft()) - .withUsageMethod(getUsageMethod(openRaoContingencyId.getRight(), isSchemeRemedialAction, instant, remedialActionType)).add()); - - alterations.addAll(ignoredContingenciesMessages); - } - } - private Instant defineInstant(boolean isSchemeRemedialAction, PropertyBag parentRemedialActionPropertyBag, String remedialActionId, int durationLimit) { + if (CsaProfileConstants.RemedialActionKind.PREVENTIVE.toString().equals(parentRemedialActionPropertyBag.get(CsaProfileConstants.KIND))) { + return crac.getPreventiveInstant(); + } if (isSchemeRemedialAction) { return crac.getInstant(InstantKind.AUTO); } @@ -269,17 +208,13 @@ private Instant defineInstant(boolean isSchemeRemedialAction, PropertyBag parent if (timeToImplement == null) { return crac.getInstant(InstantKind.CURATIVE); } - int durationInSeconds = 0; + int durationInSeconds; try { durationInSeconds = CsaProfileCracUtils.convertDurationToSeconds(timeToImplement); } catch (RuntimeException e) { throw new OpenRaoImportException(ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action " + remedialActionId + " will not be imported because of an irregular timeToImplement pattern"); } - if (durationInSeconds <= durationLimit) { - return crac.getInstant(InstantKind.AUTO); - } else { - return crac.getInstant(InstantKind.CURATIVE); - } + return durationInSeconds <= durationLimit ? crac.getInstant(InstantKind.AUTO) : crac.getInstant(InstantKind.CURATIVE); } private UsageMethod getUsageMethod(CsaProfileConstants.ElementCombinationConstraintKind elementCombinationConstraintKind, boolean isSchemeRemedialAction, Instant instant, RemedialActionType remedialActionType) { diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/ElementaryActionsHelper.java b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/ElementaryActionsHelper.java index 3113e08046..682633ea09 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/ElementaryActionsHelper.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/ElementaryActionsHelper.java @@ -64,20 +64,20 @@ public ElementaryActionsHelper(PropertyBags gridStateAlterationRemedialActionPro this.gridStateAlterationCollectionPropertyBags = gridStateAlterationCollectionPropertyBags; this.assessedElementWithRemedialActionPropertyBags = assessedElementWithRemedialActionPropertyBags; - this.remedialActionDependenciesByGroup = CsaProfileCracUtils.getMappedPropertyBagsSet(remedialActionDependenciesPropertyBags, CsaProfileConstants.DEPENDING_REMEDIAL_ACTION_GROUP); + this.remedialActionDependenciesByGroup = CsaProfileCracUtils.groupPropertyBags(remedialActionDependenciesPropertyBags, CsaProfileConstants.DEPENDING_REMEDIAL_ACTION_GROUP); - this.linkedContingencyWithRAs = CsaProfileCracUtils.getMappedPropertyBagsSet(contingencyWithRemedialActionsPropertyBags, CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); - this.linkedStaticPropertyRanges = CsaProfileCracUtils.getMappedPropertyBagsSet(staticPropertyRangesPropertyBags, CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); // the id here is the id of the subclass of gridStateAlteration (tapPositionAction, RotatingMachine, ..) + this.linkedContingencyWithRAs = CsaProfileCracUtils.groupPropertyBags(contingencyWithRemedialActionsPropertyBags, CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); + this.linkedStaticPropertyRanges = CsaProfileCracUtils.groupPropertyBags(staticPropertyRangesPropertyBags, CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); // the id here is the id of the subclass of gridStateAlteration (tapPositionAction, RotatingMachine, ..) - this.linkedTopologyActions = CsaProfileCracUtils.getMappedPropertyBagsSet(filterElementaryActions(topologyActionsPropertyBags, false), CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); - this.linkedRotatingMachineActions = CsaProfileCracUtils.getMappedPropertyBagsSet(filterElementaryActions(rotatingMachineActionsPropertyBags, false), CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); - this.linkedShuntCompensatorModification = CsaProfileCracUtils.getMappedPropertyBagsSet(filterElementaryActions(shuntCompensatorModificationPropertyBags, false), CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); - this.linkedTapPositionActions = CsaProfileCracUtils.getMappedPropertyBagsSet(filterElementaryActions(tapPositionActionsPropertyBags, false), CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); + this.linkedTopologyActions = CsaProfileCracUtils.groupPropertyBags(filterElementaryActions(topologyActionsPropertyBags, false), CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); + this.linkedRotatingMachineActions = CsaProfileCracUtils.groupPropertyBags(filterElementaryActions(rotatingMachineActionsPropertyBags, false), CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); + this.linkedShuntCompensatorModification = CsaProfileCracUtils.groupPropertyBags(filterElementaryActions(shuntCompensatorModificationPropertyBags, false), CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); + this.linkedTapPositionActions = CsaProfileCracUtils.groupPropertyBags(filterElementaryActions(tapPositionActionsPropertyBags, false), CsaProfileConstants.GRID_STATE_ALTERATION_REMEDIAL_ACTION); - this.linkedTopologyActionsAuto = CsaProfileCracUtils.getMappedPropertyBagsSet(filterElementaryActions(topologyActionsPropertyBags, true), CsaProfileConstants.GRID_STATE_ALTERATION_COLLECTION); - this.linkedRotatingMachineActionsAuto = CsaProfileCracUtils.getMappedPropertyBagsSet(filterElementaryActions(rotatingMachineActionsPropertyBags, true), CsaProfileConstants.GRID_STATE_ALTERATION_COLLECTION); - this.linkedShuntCompensatorModificationAuto = CsaProfileCracUtils.getMappedPropertyBagsSet(filterElementaryActions(shuntCompensatorModificationPropertyBags, true), CsaProfileConstants.GRID_STATE_ALTERATION_COLLECTION); - this.linkedTapPositionActionsAuto = CsaProfileCracUtils.getMappedPropertyBagsSet(filterElementaryActions(tapPositionActionsPropertyBags, true), CsaProfileConstants.GRID_STATE_ALTERATION_COLLECTION); + this.linkedTopologyActionsAuto = CsaProfileCracUtils.groupPropertyBags(filterElementaryActions(topologyActionsPropertyBags, true), CsaProfileConstants.GRID_STATE_ALTERATION_COLLECTION); + this.linkedRotatingMachineActionsAuto = CsaProfileCracUtils.groupPropertyBags(filterElementaryActions(rotatingMachineActionsPropertyBags, true), CsaProfileConstants.GRID_STATE_ALTERATION_COLLECTION); + this.linkedShuntCompensatorModificationAuto = CsaProfileCracUtils.groupPropertyBags(filterElementaryActions(shuntCompensatorModificationPropertyBags, true), CsaProfileConstants.GRID_STATE_ALTERATION_COLLECTION); + this.linkedTapPositionActionsAuto = CsaProfileCracUtils.groupPropertyBags(filterElementaryActions(tapPositionActionsPropertyBags, true), CsaProfileConstants.GRID_STATE_ALTERATION_COLLECTION); } diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/NetworkActionCreator.java b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/NetworkActionCreator.java index 3112d811bd..3ffaa5b3d8 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/NetworkActionCreator.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/NetworkActionCreator.java @@ -122,7 +122,7 @@ private boolean addInjectionSetPointFromRotatingMachineAction(Set s .add(); return true; } else { - alterations.add("Elementary rotating machine action on rotating machine %s for remedial action %s ignored because the RotatingMachineAction is disabled".formatted(rotatingMachineId, remedialActionId)); + alterations.add("Elementary rotating machine action on rotating machine %s for remedial action %s ignored because the RotatingMachineAction is disabled.".formatted(rotatingMachineId, remedialActionId)); return false; } } @@ -145,7 +145,7 @@ private boolean addInjectionSetPointFromShuntCompensatorModification(Set staticPropertyRa .withActionType("0".equals(normalValue) ? ActionType.CLOSE : ActionType.OPEN).add(); return true; } else { - alterations.add("Elementary topology action on switch %s for remedial action %s ignored because the TopologyAction is disabled".formatted(switchId, remedialActionId)); + alterations.add("Elementary topology action on switch %s for remedial action %s ignored because the TopologyAction is disabled.".formatted(switchId, remedialActionId)); return false; } } diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnConstraintUsageRuleHelper.java b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnConstraintUsageRuleHelper.java index 45b64c16d8..49ac06cd60 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnConstraintUsageRuleHelper.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnConstraintUsageRuleHelper.java @@ -1,97 +1,112 @@ /* - * Copyright (c) 2023, RTE (http://www.rte-france.com) + * 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/. */ package com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.remedialaction; -import com.powsybl.openrao.commons.OpenRaoException; +import com.powsybl.contingency.Contingency; +import com.powsybl.openrao.data.cracapi.Crac; +import com.powsybl.openrao.data.cracapi.cnec.Cnec; import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileConstants; import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileElementaryCreationContext; import com.powsybl.triplestore.api.PropertyBag; import com.powsybl.triplestore.api.PropertyBags; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; +import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileConstants.COMBINATION_CONSTRAINT_KIND; +import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileConstants.REQUEST_ASSESSED_ELEMENT; + /** - * @author Mohamed Ben-rejeb {@literal } + * @author Thomas Bouquet */ -public class OnConstraintUsageRuleHelper { - private final Set csaProfileCnecCreationContexts; - - private final Set importedCnecsCombinableWithRas = new HashSet<>(); - private final Map> excludedCnecsByRemedialAction = new HashMap<>(); - private final Map> includedCnecsByRemedialAction = new HashMap<>(); - private final Map> consideredCnecsByRemedialAction = new HashMap<>(); - - public OnConstraintUsageRuleHelper(Set csaProfileCnecCreationContexts, PropertyBags assessedElements, PropertyBags assessedElementsWithRemedialAction) { - this.csaProfileCnecCreationContexts = csaProfileCnecCreationContexts; - readAssessedElementsCombinableWithRemedialActions(assessedElements); - readAssessedElementsWithRemedialAction(assessedElementsWithRemedialAction); - } +public final class OnConstraintUsageRuleHelper { - private void readAssessedElementsCombinableWithRemedialActions(PropertyBags assessedElements) { - List nativeIdsOfCnecsCombinableWithRas = assessedElements.stream() - .filter(element -> element.getId(CsaProfileConstants.REQUEST_ASSESSED_ELEMENT_IS_COMBINABLE_WITH_REMEDIAL_ACTION) != null && Boolean.parseBoolean(element.getId(CsaProfileConstants.REQUEST_ASSESSED_ELEMENT_IS_COMBINABLE_WITH_REMEDIAL_ACTION))) - .map(element -> element.getId(CsaProfileConstants.REQUEST_ASSESSED_ELEMENT)).toList(); - Map> nativeToOpenRaoCnecIdsCombinableWithRas = getImportedCnecsNativeIdsToOpenRaoIds(); - nativeToOpenRaoCnecIdsCombinableWithRas.keySet().retainAll(nativeIdsOfCnecsCombinableWithRas); - nativeToOpenRaoCnecIdsCombinableWithRas.values().stream().flatMap(Set::stream).forEach(importedCnecsCombinableWithRas::add); + private OnConstraintUsageRuleHelper() { } - private void readAssessedElementsWithRemedialAction(PropertyBags assessedElementsWithRemedialAction) { - assessedElementsWithRemedialAction.stream().filter(this::checkNormalEnabled).filter(propertyBag -> getImportedCnecsNativeIdsToOpenRaoIds().containsKey(propertyBag.getId(CsaProfileConstants.REQUEST_ASSESSED_ELEMENT))).forEach(propertyBag -> { - String combinationConstraintKind = propertyBag.get(CsaProfileConstants.COMBINATION_CONSTRAINT_KIND); - String remedialActionId = propertyBag.getId(CsaProfileConstants.REQUEST_REMEDIAL_ACTION); - String assessedElementId = propertyBag.getId(CsaProfileConstants.REQUEST_ASSESSED_ELEMENT); - - Set openRaoCnecIds = getImportedCnecsNativeIdsToOpenRaoIds().get(assessedElementId); - - if (combinationConstraintKind.equals(CsaProfileConstants.ElementCombinationConstraintKind.EXCLUDED.toString())) { - excludedCnecsByRemedialAction - .computeIfAbsent(remedialActionId, k -> new HashSet<>()) - .addAll(openRaoCnecIds); - } else if (combinationConstraintKind.equals(CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED.toString())) { - includedCnecsByRemedialAction - .computeIfAbsent(remedialActionId, k -> new HashSet<>()) - .addAll(openRaoCnecIds); - } else if (combinationConstraintKind.equals(CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED.toString())) { - consideredCnecsByRemedialAction - .computeIfAbsent(remedialActionId, k -> new HashSet<>()) - .addAll(openRaoCnecIds); - } else { - throw new OpenRaoException(String.format("Unsupported combinationConstraintKind of AssessedElementsWithRemedialAction with id %s, only ['INCLUDED', 'EXCLUDED', 'CONSIDERED'] are supported ", propertyBag.get(CsaProfileConstants.REQUEST_ASSESSED_ELEMENT_WITH_REMEDIAL_ACTION))); - } - }); + public static Set getImportedCnecFromAssessedElementId(String assessedElementId, Crac crac, Set cnecCreationContexts) { + return cnecCreationContexts.stream().filter(context -> context.isImported() && assessedElementId.equals(context.getNativeId())).map(context -> crac.getCnec(context.getElementId())).collect(Collectors.toSet()); } - private boolean checkNormalEnabled(PropertyBag propertyBag) { - Optional normalEnabledOpt = Optional.ofNullable(propertyBag.get(CsaProfileConstants.NORMAL_ENABLED)); - return normalEnabledOpt.isEmpty() || Boolean.parseBoolean(normalEnabledOpt.get()); + public static Set getCnecsBuiltFromAssessedElementsCombinableWithRemedialActions(Crac crac, Set cnecCreationContexts, PropertyBags assessedElementPropertyBags) { + Set cnecsCombinableWithRemedialActions = new HashSet<>(); + assessedElementPropertyBags.stream().filter(propertyBag -> Boolean.parseBoolean(propertyBag.getId(CsaProfileConstants.REQUEST_ASSESSED_ELEMENT_IS_COMBINABLE_WITH_REMEDIAL_ACTION))).forEach(propertyBag -> cnecsCombinableWithRemedialActions.addAll(getImportedCnecFromAssessedElementId(propertyBag.getId(CsaProfileConstants.REQUEST_ASSESSED_ELEMENT), crac, cnecCreationContexts))); + return cnecsCombinableWithRemedialActions; } - private Map> getImportedCnecsNativeIdsToOpenRaoIds() { - return csaProfileCnecCreationContexts.stream() - .filter(CsaProfileElementaryCreationContext::isImported) - .collect(Collectors.groupingBy( - CsaProfileElementaryCreationContext::getNativeId, - Collectors.mapping(CsaProfileElementaryCreationContext::getElementId, Collectors.toSet()) - )); + public static Set filterCnecsThatHaveGivenContingencies(Set cnecs, Set contingenciesIds) { + return cnecs.stream().filter(cnec -> cnec.getState().getContingency().isPresent() && contingenciesIds.contains(cnec.getState().getContingency().get().getId())).collect(Collectors.toSet()); } - public Set getImportedCnecsCombinableWithRas() { - return importedCnecsCombinableWithRas; - } + public static Map processCnecsLinkedToRemedialAction(Crac crac, String remedialActionId, PropertyBags assessedElementPropertyBags, Set linkedAssessedElementWithRemedialActions, Set linkedContingencyWithRemedialActions, Set cnecCreationContexts) { + Map contingencyStatusMap = OnContingencyStateUsageRuleHelper.processContingenciesLinkedToRemedialAction(crac, remedialActionId, linkedContingencyWithRemedialActions); + Map cnecStatusMap = new HashMap<>(); + Set cnecsCombinableWithRemedialAction = getCnecsBuiltFromAssessedElementsCombinableWithRemedialActions(crac, cnecCreationContexts, assessedElementPropertyBags); + Map validContingenciesUsageRules = contingencyStatusMap.entrySet().stream().filter(entry -> entry.getValue().isValid()).collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().elementCombinationConstraintKind())); - public Map> getExcludedCnecsByRemedialAction() { - return this.excludedCnecsByRemedialAction; - } + for (PropertyBag assessedElementWithRemedialActionPropertyBag : linkedAssessedElementWithRemedialActions) { + String assessedElementId = assessedElementWithRemedialActionPropertyBag.getId(REQUEST_ASSESSED_ELEMENT); + Set cnecs = contingencyStatusMap.isEmpty() ? getImportedCnecFromAssessedElementId(assessedElementId, crac, cnecCreationContexts) : filterCnecsThatHaveGivenContingencies(getImportedCnecFromAssessedElementId(assessedElementId, crac, cnecCreationContexts), validContingenciesUsageRules.keySet()); + + if (cnecStatusMap.containsKey(assessedElementId) || cnecs.stream().anyMatch(cnec -> cnecStatusMap.containsKey(cnec.getId()))) { + cnecStatusMap.put(assessedElementId, new AssociationStatus(false, null, "OnConstraint usage rule for remedial action %s with assessed element %s ignored because this assessed element has several conflictual links to the remedial action.".formatted(remedialActionId, assessedElementId))); + cnecs.stream().map(Cnec::getId).forEach(cnecStatusMap::remove); + continue; + } + + String combinationConstraintKindStr = assessedElementWithRemedialActionPropertyBag.get(COMBINATION_CONSTRAINT_KIND); + Optional normalEnabledOpt = Optional.ofNullable(assessedElementWithRemedialActionPropertyBag.get(CsaProfileConstants.NORMAL_ENABLED)); + + cnecsCombinableWithRemedialAction.removeAll(cnecs); + + if (cnecs.isEmpty()) { + cnecStatusMap.put(assessedElementId, new AssociationStatus(false, null, "OnConstraint usage rule for remedial action %s with assessed element %s ignored because no CNEC was imported by Open RAO from this assessed element.".formatted(remedialActionId, assessedElementId))); + continue; + } + + if (normalEnabledOpt.isPresent() && !Boolean.parseBoolean(normalEnabledOpt.get())) { + cnecStatusMap.put(assessedElementId, new AssociationStatus(false, null, "OnConstraint usage rule for remedial action %s with assessed element %s ignored because the association is disabled.".formatted(remedialActionId, assessedElementId))); + continue; + } + + if (!combinationConstraintKindStr.equals(CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED.toString()) && !combinationConstraintKindStr.equals(CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED.toString())) { + cnecStatusMap.put(assessedElementId, new AssociationStatus(false, null, "OnConstraint usage rule for remedial action %s with assessed element %s ignored because of an illegal combinationConstraintKind.".formatted(remedialActionId, assessedElementId))); + continue; + } + + CsaProfileConstants.ElementCombinationConstraintKind combinationConstraintKind = CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED.toString().equals(combinationConstraintKindStr) ? CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED : CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED; + + cnecs.forEach( + cnec -> { + if (cnec.getState().getContingency().isPresent()) { + Contingency contingency = cnec.getState().getContingency().get(); + if (validContingenciesUsageRules.containsKey(contingency.getId()) && combinationConstraintKind != validContingenciesUsageRules.get(contingency.getId())) { + cnecStatusMap.put(cnec.getId(), new AssociationStatus(false, null, "OnConstraint usage rule for remedial action %s with CNEC %s ignored because the combinationConstraintKinds between of the AssessedElementWithRemedialAction for assessed element %s and the ContingencyWithRemedialAction for contingency %s are different.".formatted(remedialActionId, cnec.getId(), assessedElementId, contingency.getId()))); + return; + } + } + cnecStatusMap.put(cnec.getId(), new AssociationStatus(true, combinationConstraintKind, "")); + } + ); + } + + // Add CNECs built from AssessedElements which are combinable with remedial actions + if (contingencyStatusMap.isEmpty()) { + // The remedial action is not associated to contingencies + cnecsCombinableWithRemedialAction.forEach(cnec -> cnecStatusMap.put(cnec.getId(), new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, ""))); + } else { + // The remedial action is associated to contingencies (valid or not) + filterCnecsThatHaveGivenContingencies(cnecsCombinableWithRemedialAction, validContingenciesUsageRules.keySet()).forEach(cnec -> cnecStatusMap.put(cnec.getId(), new AssociationStatus(true, validContingenciesUsageRules.get(cnec.getState().getContingency().orElseThrow().getId()), ""))); + } - public Map> getConsideredAndIncludedCnecsByRemedialAction() { - Map> result = new HashMap<>(this.includedCnecsByRemedialAction); - result.putAll(consideredCnecsByRemedialAction); - return result; + return cnecStatusMap; } } diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnContingencyStateUsageRuleHelper.java b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnContingencyStateUsageRuleHelper.java new file mode 100644 index 0000000000..44f2adb071 --- /dev/null +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnContingencyStateUsageRuleHelper.java @@ -0,0 +1,64 @@ +/* + * 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/. + */ +package com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.remedialaction; + +import com.powsybl.contingency.Contingency; +import com.powsybl.openrao.data.cracapi.Crac; +import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileConstants; +import com.powsybl.triplestore.api.PropertyBag; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileConstants.COMBINATION_CONSTRAINT_KIND; +import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileConstants.REQUEST_CONTINGENCY; + +/** + * @author Thomas Bouquet + */ +public final class OnContingencyStateUsageRuleHelper { + + private OnContingencyStateUsageRuleHelper() { } + + public static Map processContingenciesLinkedToRemedialAction(Crac crac, String remedialActionId, Set linkedContingencyWithRemedialActions) { + Map contingencyStatusMap = new HashMap<>(); + + for (PropertyBag contingencyWithRemedialActionPropertyBag : linkedContingencyWithRemedialActions) { + String contingencyId = contingencyWithRemedialActionPropertyBag.getId(REQUEST_CONTINGENCY); + + if (contingencyStatusMap.containsKey(contingencyId)) { + contingencyStatusMap.put(contingencyId, new AssociationStatus(false, null, "OnContingencyState usage rule for remedial action %s with contingency %s ignored because this contingency has several conflictual links to the remedial action.".formatted(remedialActionId, contingencyId))); + continue; + } + + Contingency contingency = crac.getContingency(contingencyId); + String combinationConstraintKindStr = contingencyWithRemedialActionPropertyBag.get(COMBINATION_CONSTRAINT_KIND); + Optional normalEnabledOpt = Optional.ofNullable(contingencyWithRemedialActionPropertyBag.get(CsaProfileConstants.NORMAL_ENABLED)); + + if (contingency == null) { + contingencyStatusMap.put(contingencyId, new AssociationStatus(false, null, "OnContingencyState usage rule for remedial action %s with contingency %s ignored because this contingency does not exist or was not imported by Open RAO.".formatted(remedialActionId, contingencyId))); + continue; + } + + if (normalEnabledOpt.isPresent() && !Boolean.parseBoolean(normalEnabledOpt.get())) { + contingencyStatusMap.put(contingencyId, new AssociationStatus(false, null, "OnContingencyState usage rule for remedial action %s with contingency %s ignored because the association is disabled.".formatted(remedialActionId, contingencyId))); + continue; + } + + if (!combinationConstraintKindStr.equals(CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED.toString()) && !combinationConstraintKindStr.equals(CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED.toString())) { + contingencyStatusMap.put(contingencyId, new AssociationStatus(false, null, "OnContingencyState usage rule for remedial action %s with contingency %s ignored because of an illegal combinationConstraintKind.".formatted(remedialActionId, contingencyId))); + continue; + } + + contingencyStatusMap.put(contingencyId, new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED.toString().equals(combinationConstraintKindStr) ? CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED : CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, "")); + } + + return contingencyStatusMap; + } +} diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/PstRangeActionCreator.java b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/PstRangeActionCreator.java index 2e8ed3c7b3..5dd0b88bc4 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/PstRangeActionCreator.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/PstRangeActionCreator.java @@ -32,7 +32,7 @@ public PstRangeActionCreator(Crac crac, Network network) { this.network = network; } - public PstRangeActionAdder getPstRangeActionAdder(Map> linkedTapPositionActions, Map> linkedStaticPropertyRanges, String remedialActionId, String elementaryActionsAggregatorId, List alterations) { + public PstRangeActionAdder getPstRangeActionAdder(Map> linkedTapPositionActions, Map> linkedStaticPropertyRanges, String remedialActionId, String elementaryActionsAggregatorId) { PstRangeActionAdder pstRangeActionAdder = crac.newPstRangeAction().withId(remedialActionId); if (linkedTapPositionActions.containsKey(elementaryActionsAggregatorId)) { diff --git a/data/crac-creation/crac-creator-csa-profiles/src/main/resources/csa_profile.sparql b/data/crac-creation/crac-creator-csa-profiles/src/main/resources/csa_profile.sparql index c1b59181fc..29c81b67d3 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/main/resources/csa_profile.sparql +++ b/data/crac-creation/crac-creator-csa-profiles/src/main/resources/csa_profile.sparql @@ -273,8 +273,7 @@ SELECT * GRAPH <%s> { ?contingencyWithRemedialAction rdf:type nc:ContingencyWithRemedialAction ; - nc:ContingencyWithRemedialAction.mRID ?mRID ; - nc:ContingencyWithRemedialAction.RemedialAction ?gridStateAlterationRemedialAction ; + nc:ContingencyWithRemedialAction.RemedialAction ?remedialAction ; nc:ContingencyWithRemedialAction.Contingency ?contingency ; nc:ContingencyWithRemedialAction.combinationConstraintKind ?combinationConstraintKind ; diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracCreationTestUtil.java b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracCreationTestUtil.java index 1c6847d5dc..65580bc41d 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracCreationTestUtil.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracCreationTestUtil.java @@ -186,6 +186,10 @@ public static void assertHasOnFlowConstraintUsageRule(CsaProfileCracCreationCont ); } + public static void assertHasOnFlowConstraintUsageRule(CsaProfileCracCreationContext cracCreationContext, String raId, String flowCnecId, String instant, UsageMethod usageMethod) { + assertHasOnFlowConstraintUsageRule(cracCreationContext, raId, flowCnecId, cracCreationContext.getCrac().getInstant(instant), usageMethod); + } + public static void assertHasOnAngleConstraintUsageRule(CsaProfileCracCreationContext cracCreationContext, String raId, String angleCnecId, Instant instant, UsageMethod usageMethod) { assertTrue( cracCreationContext.getCrac().getRemedialAction(raId).getUsageRules().stream().filter(OnAngleConstraint.class::isInstance) diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracUtilsTest.java b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracUtilsTest.java index 560924958c..e3b5343b9e 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracUtilsTest.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/CsaProfileCracUtilsTest.java @@ -51,7 +51,7 @@ void testGetLinkedPropertyBags() { sourcesPb.addAll(Arrays.asList(sourcePb1, sourcePb2, sourcePb3)); - Map> map = CsaProfileCracUtils.getMappedPropertyBagsSet(sourcesPb, "sourceProperty2"); + Map> map = CsaProfileCracUtils.groupPropertyBags(sourcesPb, "sourceProperty2"); Set result = map.get(destPb.getId("destProperty3")); assertNotNull(result); assertEquals(1, result.size()); diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/AngleCnecCreationTest.java b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/AngleCnecCreationTest.java index cbcb3b308e..372b2addff 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/AngleCnecCreationTest.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/AngleCnecCreationTest.java @@ -148,7 +148,7 @@ void importAngleCnecs() { assertCnecNotImported(cracCreationContext, "assessed-element-12", ImportStatus.INCONSISTENCY_IN_DATA, "AssessedElement assessed-element-12 ignored because the network element FFR1AA1 _generator is not a bus bar section"); assertCnecNotImported(cracCreationContext, "assessed-element-13", ImportStatus.INCONSISTENCY_IN_DATA, "AssessedElement assessed-element-13 ignored because the network element FFR2AA1 _generator is not a bus bar section"); - assertHasOnAngleConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.AVAILABLE); + assertHasOnAngleConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.FORCED); assertHasOnAngleConstraintUsageRule(cracCreationContext, "remedial-action-2", "RTE_AE2 (assessed-element-2) - RTE_CO1 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.AVAILABLE); assertHasOnAngleConstraintUsageRule(cracCreationContext, "remedial-action-2", "RTE_AE2 (assessed-element-2) - RTE_CO2 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.AVAILABLE); } diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/FlowCnecCreationTest.java b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/FlowCnecCreationTest.java index 567e6be4ba..0a527f2a36 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/FlowCnecCreationTest.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/FlowCnecCreationTest.java @@ -300,7 +300,7 @@ void importFlowCnecs() { assertCnecNotImported(cracCreationContext, "assessed-element-13", ImportStatus.INCONSISTENCY_IN_DATA, "AssessedElement assessed-element-13 ignored because the assessed element is not in base case and not combinable with contingencies, but no explicit link to a contingency was found"); assertCnecNotImported(cracCreationContext, "assessed-element-14", ImportStatus.INCONSISTENCY_IN_DATA, "AssessedElement assessed-element-14 ignored because the network element FFR1AA1 _generator is not a branch"); - assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.FORCED); assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-2", "RTE_AE4 (assessed-element-4) - RTE_CO1 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.AVAILABLE); assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-2", "RTE_AE4 (assessed-element-4) - RTE_CO2 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.AVAILABLE); } diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/VoltageCnecCreationTest.java b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/VoltageCnecCreationTest.java index 39c30eb3ed..5678935b01 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/VoltageCnecCreationTest.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/cnec/VoltageCnecCreationTest.java @@ -77,8 +77,8 @@ void importVoltageCnecs() { assertCnecNotImported(cracCreationContext, "assessed-element-8", ImportStatus.INCOMPLETE_DATA, "AssessedElement assessed-element-8 ignored because no ConductingEquipment or OperationalLimit was provided"); assertHasOnVoltageConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.AVAILABLE); - assertHasOnVoltageConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE2 (assessed-element-2) - RTE_CO1 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.AVAILABLE); - assertHasOnVoltageConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE2 (assessed-element-2) - RTE_CO2 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.AVAILABLE); + assertHasOnVoltageConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE2 (assessed-element-2) - RTE_CO1 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.FORCED); + assertHasOnVoltageConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE2 (assessed-element-2) - RTE_CO2 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.FORCED); assertHasOnVoltageConstraintUsageRule(cracCreationContext, "remedial-action-2", "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative", cracCreationContext.getCrac().getInstant(CURATIVE_INSTANT_ID), UsageMethod.AVAILABLE); } } diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/AutoRemedialActionTest.java b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/AutoRemedialActionTest.java index 837306f8bc..c1e2f39eac 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/AutoRemedialActionTest.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/AutoRemedialActionTest.java @@ -62,19 +62,18 @@ void importAutoRemedialActions() { assertEquals(InstantKind.AUTO, networkSpsUsageRule.getInstant().getKind()); assertEquals(UsageMethod.FORCED, networkSpsUsageRule.getUsageMethod()); - assertEquals(12, cracCreationContext.getRemedialActionCreationContexts().stream().filter(ra -> !ra.isImported()).toList().size()); + assertEquals(11, cracCreationContext.getRemedialActionCreationContexts().stream().filter(ra -> !ra.isImported()).toList().size()); assertRaNotImported(cracCreationContext, "sps-with-multiple-remedial-action-schemes", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action sps-with-multiple-remedial-action-schemes will not be imported because it has several conflictual RemedialActionSchemes"); assertRaNotImported(cracCreationContext, "sps-with-multiple-stages", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action sps-with-multiple-stages will not be imported because it has several conflictual Stages"); assertRaNotImported(cracCreationContext, "pst-sps-without-speed", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action pst-sps-without-speed will not be imported because an auto PST range action must have a speed defined"); assertRaNotImported(cracCreationContext, "preventive-sps", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action preventive-sps will not be imported because auto remedial action must be of curative kind"); - assertRaNotImported(cracCreationContext, "not-forced-sps", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action not-forced-sps will not be imported because it must be linked to the contingency contingency with an 'included' ElementCombinationConstraintKind"); assertRaNotImported(cracCreationContext, "sps-with-unarmed-remedial-action-scheme", ImportStatus.NOT_FOR_RAO, "Remedial action sps-with-unarmed-remedial-action-scheme will not be imported because RemedialActionScheme 5f7796db-a662-488d-a938-39c8a2b36055 is not armed"); assertRaNotImported(cracCreationContext, "not-sips-sps", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action not-sips-sps will not be imported because of an unsupported kind for remedial action schedule (only SIPS allowed)"); assertRaNotImported(cracCreationContext, "sps-without-remedial-action-scheme", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action sps-without-remedial-action-scheme will not be imported because it has no associated RemedialActionScheme"); assertRaNotImported(cracCreationContext, "sps-without-stage", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action sps-without-stage will not be imported because it has no associated Stage"); assertRaNotImported(cracCreationContext, "sps-without-grid-state-alteration-collection", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action sps-without-grid-state-alteration-collection will not be imported because it has no associated GridStateAlterationCollection"); assertRaNotImported(cracCreationContext, "sps-without-elementary-actions", ImportStatus.NOT_FOR_RAO, "Remedial action sps-without-elementary-actions will not be imported because it has no elementary action"); - assertRaNotImported(cracCreationContext, "sps-without-contingency", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action sps-without-contingency will not be imported because no contingency is linked to the remedial action"); + assertRaNotImported(cracCreationContext, "sps-without-contingency", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action sps-without-contingency will not be imported because no contingency or assessed element is linked to the remedial action and this is nor supported for ARAs"); } } diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/GroupRemedialActionTest.java b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/GroupRemedialActionTest.java index 99e4b2ede7..0ac3bb7dcc 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/GroupRemedialActionTest.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/GroupRemedialActionTest.java @@ -62,7 +62,7 @@ void importGroupedRemedialActions() { UsageRule ur3 = cracCreationContext.getCrac().getNetworkAction("7d2833e4-c5a8-4d79-b936-c735a58f1774").getUsageRules().iterator().next(); assertTrue(ur3 instanceof OnFlowConstraint); assertEquals(InstantKind.CURATIVE, ur3.getInstant().getKind()); - assertEquals(UsageMethod.AVAILABLE, ur3.getUsageMethod()); + assertEquals(UsageMethod.FORCED, ur3.getUsageMethod()); assertEquals("RTE_AE1 (f7708112-b880-4674-98a1-b005a01a61d5) - RTE_CO1 - curative", ((OnFlowConstraint) ur3).getFlowCnec().getId()); assertNetworkActionImported(cracCreationContext, "66979f64-3c52-486c-84f7-b5439cd71765", Set.of("BBE1AA1 BBE4AA1 1"), true, 1, "RTE"); diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnConstraintUsageRuleHelperTest.java b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnConstraintUsageRuleHelperTest.java new file mode 100644 index 0000000000..e82fffc64a --- /dev/null +++ b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnConstraintUsageRuleHelperTest.java @@ -0,0 +1,305 @@ +/* + * 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/. + */ +package com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.remedialaction; + +import com.powsybl.openrao.commons.Unit; +import com.powsybl.openrao.data.cracapi.Crac; +import com.powsybl.openrao.data.cracapi.InstantKind; +import com.powsybl.openrao.data.cracapi.cnec.Side; +import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileConstants; +import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileElementaryCreationContext; +import com.powsybl.openrao.data.cracimpl.CracImpl; +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.PropertyBags; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Thomas Bouquet + */ +class OnConstraintUsageRuleHelperTest { + private Crac crac; + private Set cnecCreationContexts; + private PropertyBags assessedElementPropertyBags; + private Set assessedElementWithRemedialActions; + private Set contingencyWithRemedialActions; + + @BeforeEach + void setUp() { + crac = new CracImpl("crac"); + + // Set-up instants + + crac.newInstant("preventive", InstantKind.PREVENTIVE); + crac.newInstant("outage", InstantKind.OUTAGE); + crac.newInstant("auto", InstantKind.AUTO); + crac.newInstant("curative", InstantKind.CURATIVE); + + // Add contingencies + + crac.newContingency().withId("contingency-1").add(); + crac.newContingency().withId("contingency-2").add(); + crac.newContingency().withId("contingency-3").add(); + crac.newContingency().withId("contingency-4").add(); + crac.newContingency().withId("contingency-5").add(); + crac.newContingency().withId("contingency-6").add(); + + // Add FlowCNECs + + crac.newFlowCnec().withId("Line 1 - curative - CO1").withNominalVoltage(400d).withNetworkElement("Line 1").withInstant("curative").withContingency("contingency-1").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 2 - preventive").withNominalVoltage(400d).withNetworkElement("Line 2").withInstant("preventive").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 2 - curative - CO1").withNominalVoltage(400d).withNetworkElement("Line 2").withInstant("curative").withContingency("contingency-1").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 2 - curative - CO2").withNominalVoltage(400d).withNetworkElement("Line 2").withInstant("curative").withContingency("contingency-2").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 2 - curative - CO3").withNominalVoltage(400d).withNetworkElement("Line 2").withInstant("curative").withContingency("contingency-3").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 2 - curative - CO4").withNominalVoltage(400d).withNetworkElement("Line 2").withInstant("curative").withContingency("contingency-4").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 2 - curative - CO5").withNominalVoltage(400d).withNetworkElement("Line 2").withInstant("curative").withContingency("contingency-5").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 2 - curative - CO6").withNominalVoltage(400d).withNetworkElement("Line 2").withInstant("curative").withContingency("contingency-6").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 3 - preventive").withNominalVoltage(400d).withNetworkElement("Line 3").withInstant("preventive").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 3 - curative - CO1").withNominalVoltage(400d).withNetworkElement("Line 3").withInstant("curative").withContingency("contingency-1").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 3 - curative - CO2").withNominalVoltage(400d).withNetworkElement("Line 3").withInstant("curative").withContingency("contingency-2").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 3 - curative - CO3").withNominalVoltage(400d).withNetworkElement("Line 3").withInstant("curative").withContingency("contingency-3").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 3 - curative - CO4").withNominalVoltage(400d).withNetworkElement("Line 3").withInstant("curative").withContingency("contingency-4").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 3 - curative - CO5").withNominalVoltage(400d).withNetworkElement("Line 3").withInstant("curative").withContingency("contingency-5").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 3 - curative - CO6").withNominalVoltage(400d).withNetworkElement("Line 3").withInstant("curative").withContingency("contingency-6").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 4 - curative - CO1").withNominalVoltage(400d).withNetworkElement("Line 4").withInstant("curative").withContingency("contingency-1").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 6 - curative - CO1").withNominalVoltage(400d).withNetworkElement("Line 6").withInstant("curative").withContingency("contingency-1").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + crac.newFlowCnec().withId("Line 8 - curative - CO1").withNominalVoltage(400d).withNetworkElement("Line 8").withInstant("curative").withContingency("contingency-1").newThreshold().withSide(Side.LEFT).withMax(1000d).withUnit(Unit.AMPERE).add().add(); + + // Add CNEC creation contexts + + cnecCreationContexts = new HashSet<>(); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-1", "Line 1 - curative - CO1", "Line 1 - curative - CO1", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-2", "Line 2 - preventive", "Line 2 - preventive", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-2", "Line 2 - curative - CO1", "Line 2 - curative - CO1", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-2", "Line 2 - curative - CO2", "Line 2 - curative - CO2", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-2", "Line 2 - curative - CO3", "Line 2 - curative - CO3", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-2", "Line 2 - curative - CO4", "Line 2 - curative - CO4", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-2", "Line 2 - curative - CO5", "Line 2 - curative - CO5", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-2", "Line 2 - curative - CO6", "Line 2 - curative - CO6", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-3", "Line 3 - preventive", "Line 3 - preventive", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-3", "Line 3 - curative - CO1", "Line 3 - curative - CO1", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-3", "Line 3 - curative - CO2", "Line 3 - curative - CO2", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-3", "Line 3 - curative - CO3", "Line 3 - curative - CO3", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-3", "Line 3 - curative - CO4", "Line 3 - curative - CO4", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-3", "Line 3 - curative - CO5", "Line 3 - curative - CO5", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-3", "Line 3 - curative - CO6", "Line 3 - curative - CO6", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-4", "Line 4 - curative - CO1", "Line 4 - curative - CO1", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-6", "Line 6 - curative - CO1", "Line 6 - curative - CO1", "", false)); + cnecCreationContexts.add(CsaProfileElementaryCreationContext.imported("assessed-element-8", "Line 8 - curative - CO1", "Line 8 - curative - CO1", "", false)); + + // Add AssessedElement property bags + + PropertyBag assessedElement1PropertyBag = new PropertyBag(List.of("assessedElement", "isCombinableWithRemedialAction"), true, false); + assessedElement1PropertyBag.put("assessedElement", "_assessed-element-1"); + assessedElement1PropertyBag.put("isCombinableWithRemedialAction", "false"); + + PropertyBag assessedElement2PropertyBag = new PropertyBag(List.of("assessedElement", "isCombinableWithRemedialAction"), true, false); + assessedElement2PropertyBag.put("assessedElement", "_assessed-element-2"); + assessedElement2PropertyBag.put("isCombinableWithRemedialAction", "true"); + + PropertyBag assessedElement3PropertyBag = new PropertyBag(List.of("assessedElement", "isCombinableWithRemedialAction"), true, false); + assessedElement3PropertyBag.put("assessedElement", "_assessed-element-3"); + assessedElement3PropertyBag.put("isCombinableWithRemedialAction", "true"); + + PropertyBag assessedElement4PropertyBag = new PropertyBag(List.of("assessedElement", "isCombinableWithRemedialAction"), true, false); + assessedElement4PropertyBag.put("assessedElement", "_assessed-element-4"); + assessedElement4PropertyBag.put("isCombinableWithRemedialAction", "false"); + + PropertyBag assessedElement5PropertyBag = new PropertyBag(List.of("assessedElement", "isCombinableWithRemedialAction"), true, false); + assessedElement5PropertyBag.put("assessedElement", "_assessed-element-5"); + assessedElement5PropertyBag.put("isCombinableWithRemedialAction", "false"); + + PropertyBag assessedElement6PropertyBag = new PropertyBag(List.of("assessedElement", "isCombinableWithRemedialAction"), true, false); + assessedElement6PropertyBag.put("assessedElement", "_assessed-element-6"); + assessedElement6PropertyBag.put("isCombinableWithRemedialAction", "false"); + + PropertyBag assessedElement8PropertyBag = new PropertyBag(List.of("assessedElement", "isCombinableWithRemedialAction"), true, false); + assessedElement8PropertyBag.put("assessedElement", "_assessed-element-8"); + assessedElement8PropertyBag.put("isCombinableWithRemedialAction", "false"); + + assessedElementPropertyBags = new PropertyBags(Set.of(assessedElement1PropertyBag, assessedElement2PropertyBag, assessedElement3PropertyBag, assessedElement4PropertyBag, assessedElement5PropertyBag, assessedElement6PropertyBag, assessedElement8PropertyBag)); + + // Add AssessedElementWithRemedialAction property bags + + PropertyBag ae1WithRaPropertyBag = new PropertyBag(List.of("assessedElementWithRemedialAction", "remedialAction", "assessedElement", "combinationConstraintKind"), true, false); + ae1WithRaPropertyBag.put("assessedElementWithRemedialAction", "_ae1xra"); + ae1WithRaPropertyBag.put("remedialAction", "remedial-action"); + ae1WithRaPropertyBag.put("assessedElement", "assessed-element-1"); + ae1WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + + PropertyBag ae2WithRaPropertyBag = new PropertyBag(List.of("assessedElementWithRemedialAction", "remedialAction", "assessedElement", "combinationConstraintKind", "normalEnabled"), true, false); + ae2WithRaPropertyBag.put("assessedElementWithRemedialAction", "_ae2xra"); + ae2WithRaPropertyBag.put("remedialAction", "remedial-action"); + ae2WithRaPropertyBag.put("assessedElement", "assessed-element-2"); + ae2WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + ae2WithRaPropertyBag.put("normalEnabled", "true"); + + PropertyBag ae4WithRaPropertyBag = new PropertyBag(List.of("assessedElementWithRemedialAction", "remedialAction", "assessedElement", "combinationConstraintKind", "normalEnabled"), true, false); + ae4WithRaPropertyBag.put("assessedElementWithRemedialAction", "_ae4xra"); + ae4WithRaPropertyBag.put("remedialAction", "remedial-action"); + ae4WithRaPropertyBag.put("assessedElement", "assessed-element-4"); + ae4WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.considered"); + ae4WithRaPropertyBag.put("normalEnabled", "false"); + + PropertyBag ae5WithRaPropertyBag1 = new PropertyBag(List.of("assessedElementWithRemedialAction", "remedialAction", "assessedElement", "combinationConstraintKind"), true, false); + ae5WithRaPropertyBag1.put("assessedElementWithRemedialAction", "_ae5xra-included"); + ae5WithRaPropertyBag1.put("remedialAction", "remedial-action"); + ae5WithRaPropertyBag1.put("assessedElement", "assessed-element-5"); + ae5WithRaPropertyBag1.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + ae5WithRaPropertyBag1.setResourceNames(List.of("remedialAction", "assessedElement", "combinationConstraintKind")); + + PropertyBag ae5WithRaPropertyBag2 = new PropertyBag(List.of("assessedElementWithRemedialAction", "remedialAction", "assessedElement", "combinationConstraintKind"), true, false); + ae5WithRaPropertyBag2.put("assessedElementWithRemedialAction", "_ae5xra-considered"); + ae5WithRaPropertyBag2.put("remedialAction", "remedial-action"); + ae5WithRaPropertyBag2.put("assessedElement", "assessed-element-5"); + ae5WithRaPropertyBag2.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.considered"); + ae5WithRaPropertyBag2.setResourceNames(List.of("remedialAction", "assessedElement", "combinationConstraintKind")); + + PropertyBag ae6WithRaPropertyBag = new PropertyBag(List.of("assessedElementWithRemedialAction", "remedialAction", "assessedElement", "combinationConstraintKind"), true, false); + ae6WithRaPropertyBag.put("assessedElementWithRemedialAction", "_ae6xra"); + ae6WithRaPropertyBag.put("remedialAction", "remedial-action"); + ae6WithRaPropertyBag.put("assessedElement", "assessed-element-6"); + ae6WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.excluded"); + ae6WithRaPropertyBag.setResourceNames(List.of("remedialAction", "assessedElement", "combinationConstraintKind")); + + PropertyBag ae7WithRaPropertyBag = new PropertyBag(List.of("assessedElementWithRemedialAction", "remedialAction", "assessedElement", "combinationConstraintKind"), true, false); + ae7WithRaPropertyBag.put("assessedElementWithRemedialAction", "_ae7xra"); + ae7WithRaPropertyBag.put("remedialAction", "remedial-action"); + ae7WithRaPropertyBag.put("assessedElement", "assessed-element-7"); + ae7WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + + PropertyBag ae8WithRaPropertyBagIncluded = new PropertyBag(List.of("assessedElementWithRemedialAction", "remedialAction", "assessedElement", "combinationConstraintKind"), true, false); + ae8WithRaPropertyBagIncluded.put("assessedElementWithRemedialAction", "_ae8xra-included"); + ae8WithRaPropertyBagIncluded.put("remedialAction", "remedial-action"); + ae8WithRaPropertyBagIncluded.put("assessedElement", "assessed-element-8"); + ae8WithRaPropertyBagIncluded.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + + PropertyBag ae8WithRaPropertyBagConsidered = new PropertyBag(List.of("assessedElementWithRemedialAction", "remedialAction", "assessedElement", "combinationConstraintKind"), true, false); + ae8WithRaPropertyBagConsidered.put("assessedElementWithRemedialAction", "_ae8xra-considered"); + ae8WithRaPropertyBagConsidered.put("remedialAction", "remedial-action"); + ae8WithRaPropertyBagConsidered.put("assessedElement", "assessed-element-8"); + ae8WithRaPropertyBagConsidered.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.considered"); + + assessedElementWithRemedialActions = Set.of(ae1WithRaPropertyBag, ae2WithRaPropertyBag, ae4WithRaPropertyBag, ae5WithRaPropertyBag1, ae5WithRaPropertyBag2, ae6WithRaPropertyBag, ae7WithRaPropertyBag, ae8WithRaPropertyBagIncluded, ae8WithRaPropertyBagConsidered); + + // Add ContingencyWithRemedialAction property bags + + PropertyBag co1WithRaPropertyBag = new PropertyBag(List.of("contingencyWithRemedialAction", "mRID", "remedialAction", "contingency", "combinationConstraintKind", "normalEnabled"), true, false); + co1WithRaPropertyBag.put("contingencyWithRemedialAction", "_co1xra"); + co1WithRaPropertyBag.put("mRID", "_co1xra"); + co1WithRaPropertyBag.put("remedialAction", "remedial-action"); + co1WithRaPropertyBag.put("contingency", "contingency-1"); + co1WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + co1WithRaPropertyBag.put("normalEnabled", "true"); + + PropertyBag co2WithRaPropertyBag = new PropertyBag(List.of("contingencyWithRemedialAction", "mRID", "remedialAction", "contingency", "combinationConstraintKind", "normalEnabled"), true, false); + co2WithRaPropertyBag.put("contingencyWithRemedialAction", "_co2xra"); + co2WithRaPropertyBag.put("mRID", "_co2xra"); + co2WithRaPropertyBag.put("remedialAction", "remedial-action"); + co2WithRaPropertyBag.put("contingency", "contingency-2"); + co2WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.considered"); + co2WithRaPropertyBag.put("normalEnabled", "true"); + + PropertyBag co3WithRaPropertyBag = new PropertyBag(List.of("contingencyWithRemedialAction", "mRID", "remedialAction", "contingency", "combinationConstraintKind", "normalEnabled"), true, false); + co3WithRaPropertyBag.put("contingencyWithRemedialAction", "_co3xra"); + co3WithRaPropertyBag.put("mRID", "_co3xra"); + co3WithRaPropertyBag.put("remedialAction", "remedial-action"); + co3WithRaPropertyBag.put("contingency", "contingency-3"); + co3WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + co3WithRaPropertyBag.put("normalEnabled", "true"); + + PropertyBag co4WithRaPropertyBag = new PropertyBag(List.of("contingencyWithRemedialAction", "mRID", "remedialAction", "contingency", "combinationConstraintKind", "normalEnabled"), true, false); + co4WithRaPropertyBag.put("contingencyWithRemedialAction", "_co4xra"); + co4WithRaPropertyBag.put("mRID", "_co4xra"); + co4WithRaPropertyBag.put("remedialAction", "remedial-action"); + co4WithRaPropertyBag.put("contingency", "contingency-4"); + co4WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.considered"); + co4WithRaPropertyBag.put("normalEnabled", "true"); + + contingencyWithRemedialActions = Set.of(co1WithRaPropertyBag, co2WithRaPropertyBag, co3WithRaPropertyBag, co4WithRaPropertyBag); + } + + @Test + void getImportedCnecFromAssessedElementId() { + assertEquals( + Set.of(crac.getFlowCnec("Line 2 - preventive"), crac.getFlowCnec("Line 2 - curative - CO1"), crac.getFlowCnec("Line 2 - curative - CO2"), crac.getFlowCnec("Line 2 - curative - CO3"), crac.getFlowCnec("Line 2 - curative - CO4"), crac.getFlowCnec("Line 2 - curative - CO5"), crac.getFlowCnec("Line 2 - curative - CO6")), + OnConstraintUsageRuleHelper.getImportedCnecFromAssessedElementId("assessed-element-2", crac, cnecCreationContexts) + ); + } + + @Test + void getCnecsBuiltFromAssessedElementsCombinableWithRemedialActions() { + assertEquals( + Set.of(crac.getFlowCnec("Line 2 - preventive"), crac.getFlowCnec("Line 2 - curative - CO1"), crac.getFlowCnec("Line 2 - curative - CO2"), crac.getFlowCnec("Line 2 - curative - CO3"), crac.getFlowCnec("Line 2 - curative - CO4"), crac.getFlowCnec("Line 2 - curative - CO5"), crac.getFlowCnec("Line 2 - curative - CO6"), crac.getFlowCnec("Line 3 - preventive"), crac.getFlowCnec("Line 3 - curative - CO1"), crac.getFlowCnec("Line 3 - curative - CO2"), crac.getFlowCnec("Line 3 - curative - CO3"), crac.getFlowCnec("Line 3 - curative - CO4"), crac.getFlowCnec("Line 3 - curative - CO5"), crac.getFlowCnec("Line 3 - curative - CO6")), + OnConstraintUsageRuleHelper.getCnecsBuiltFromAssessedElementsCombinableWithRemedialActions(crac, cnecCreationContexts, assessedElementPropertyBags) + ); + } + + @Test + void filterCnecsThatHaveGivenContingencies() { + assertEquals( + Set.of(crac.getFlowCnec("Line 2 - curative - CO3"), crac.getFlowCnec("Line 3 - curative - CO3"), crac.getFlowCnec("Line 2 - curative - CO5"), crac.getFlowCnec("Line 3 - curative - CO5")), + OnConstraintUsageRuleHelper.filterCnecsThatHaveGivenContingencies(crac.getCnecs(), Set.of("contingency-3", "contingency-5")) + ); + } + + @Test + void processCnecsLinkedToRemedialActionWithContingencies() { + Map expectedResult = new HashMap<>(); + expectedResult.put("Line 1 - curative - CO1", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 2 - curative - CO1", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 2 - curative - CO2", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with CNEC Line 2 - curative - CO2 ignored because the combinationConstraintKinds between of the AssessedElementWithRemedialAction for assessed element assessed-element-2 and the ContingencyWithRemedialAction for contingency contingency-2 are different.")); + expectedResult.put("Line 2 - curative - CO3", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 2 - curative - CO4", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with CNEC Line 2 - curative - CO4 ignored because the combinationConstraintKinds between of the AssessedElementWithRemedialAction for assessed element assessed-element-2 and the ContingencyWithRemedialAction for contingency contingency-4 are different.")); + expectedResult.put("Line 3 - curative - CO1", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 3 - curative - CO2", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, "")); + expectedResult.put("Line 3 - curative - CO3", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 3 - curative - CO4", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, "")); + expectedResult.put("assessed-element-4", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with assessed element assessed-element-4 ignored because the association is disabled.")); + expectedResult.put("assessed-element-5", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with assessed element assessed-element-5 ignored because this assessed element has several conflictual links to the remedial action.")); + expectedResult.put("assessed-element-6", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with assessed element assessed-element-6 ignored because of an illegal combinationConstraintKind.")); + expectedResult.put("assessed-element-7", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with assessed element assessed-element-7 ignored because no CNEC was imported by Open RAO from this assessed element.")); + expectedResult.put("assessed-element-8", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with assessed element assessed-element-8 ignored because this assessed element has several conflictual links to the remedial action.")); + + assertEquals(expectedResult, OnConstraintUsageRuleHelper.processCnecsLinkedToRemedialAction(crac, "remedial-action", assessedElementPropertyBags, assessedElementWithRemedialActions, contingencyWithRemedialActions, cnecCreationContexts)); + } + + @Test + void processCnecsLinkedToRemedialActionWithoutContingencies() { + Map expectedResult = new HashMap<>(); + expectedResult.put("Line 1 - curative - CO1", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 2 - preventive", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 2 - curative - CO1", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 2 - curative - CO2", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 2 - curative - CO3", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 2 - curative - CO4", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 2 - curative - CO5", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 2 - curative - CO6", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, "")); + expectedResult.put("Line 3 - preventive", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, "")); + expectedResult.put("Line 3 - curative - CO1", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, "")); + expectedResult.put("Line 3 - curative - CO2", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, "")); + expectedResult.put("Line 3 - curative - CO3", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, "")); + expectedResult.put("Line 3 - curative - CO4", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, "")); + expectedResult.put("Line 3 - curative - CO5", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, "")); + expectedResult.put("Line 3 - curative - CO6", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, "")); + expectedResult.put("assessed-element-4", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with assessed element assessed-element-4 ignored because the association is disabled.")); + expectedResult.put("assessed-element-5", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with assessed element assessed-element-5 ignored because this assessed element has several conflictual links to the remedial action.")); + expectedResult.put("assessed-element-6", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with assessed element assessed-element-6 ignored because of an illegal combinationConstraintKind.")); + expectedResult.put("assessed-element-7", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with assessed element assessed-element-7 ignored because no CNEC was imported by Open RAO from this assessed element.")); + expectedResult.put("assessed-element-8", new AssociationStatus(false, null, "OnConstraint usage rule for remedial action remedial-action with assessed element assessed-element-8 ignored because this assessed element has several conflictual links to the remedial action.")); + + assertEquals(expectedResult, OnConstraintUsageRuleHelper.processCnecsLinkedToRemedialAction(crac, "remedial-action", assessedElementPropertyBags, assessedElementWithRemedialActions, Set.of(), cnecCreationContexts)); + } +} diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnContingencyStateUsageRuleHelperTest.java b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnContingencyStateUsageRuleHelperTest.java new file mode 100644 index 0000000000..1342362629 --- /dev/null +++ b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/OnContingencyStateUsageRuleHelperTest.java @@ -0,0 +1,109 @@ +/* + * 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/. + */ +package com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.remedialaction; + +import com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileConstants; +import com.powsybl.openrao.data.cracimpl.CracImpl; +import com.powsybl.triplestore.api.PropertyBag; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Thomas Bouquet + */ +class OnContingencyStateUsageRuleHelperTest { + private final List properties = List.of("mRID", "remedialAction", "contingency", "combinationConstraintKind", "normalEnabled"); + + @Test + void processContingenciesLinkedToRemedialAction() { + CracImpl crac = new CracImpl("crac"); + crac.newContingency().withId("contingency-1").add(); + crac.newContingency().withId("contingency-2").add(); + crac.newContingency().withId("contingency-3").add(); + crac.newContingency().withId("contingency-4").add(); + crac.newContingency().withId("contingency-6").add(); + + PropertyBag co1WithRaPropertyBag = new PropertyBag(properties, true, false); + co1WithRaPropertyBag.put("mRID", "_co1xra"); + co1WithRaPropertyBag.put("remedialAction", "remedial-action"); + co1WithRaPropertyBag.put("contingency", "contingency-1"); + co1WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + co1WithRaPropertyBag.put("normalEnabled", "true"); + co1WithRaPropertyBag.setClassPropertyNames(List.of("mRID", "normalEnabled")); + co1WithRaPropertyBag.setResourceNames(List.of("remedialAction", "contingency", "combinationConstraintKind")); + + PropertyBag co2WithRaPropertyBag = new PropertyBag(List.of("mRID", "remedialAction", "contingency", "combinationConstraintKind"), false); + co2WithRaPropertyBag.put("mRID", "_co2xra"); + co2WithRaPropertyBag.put("remedialAction", "remedial-action"); + co2WithRaPropertyBag.put("contingency", "contingency-2"); + co2WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.considered"); + co2WithRaPropertyBag.setClassPropertyNames(List.of("mRID", "normalEnabled")); + co2WithRaPropertyBag.setResourceNames(List.of("remedialAction", "contingency", "combinationConstraintKind")); + + PropertyBag co3WithRaPropertyBag = new PropertyBag(properties, true, false); + co3WithRaPropertyBag.put("mRID", "_co3xra"); + co3WithRaPropertyBag.put("remedialAction", "remedial-action"); + co3WithRaPropertyBag.put("contingency", "contingency-3"); + co3WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + co3WithRaPropertyBag.put("normalEnabled", "false"); + co3WithRaPropertyBag.setClassPropertyNames(List.of("mRID", "normalEnabled")); + co3WithRaPropertyBag.setResourceNames(List.of("remedialAction", "contingency", "combinationConstraintKind")); + + PropertyBag co4WithRaPropertyBag = new PropertyBag(properties, true, false); + co4WithRaPropertyBag.put("mRID", "_co4xra"); + co4WithRaPropertyBag.put("remedialAction", "remedial-action"); + co4WithRaPropertyBag.put("contingency", "contingency-4"); + co4WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.excluded"); + co4WithRaPropertyBag.put("normalEnabled", "true"); + co4WithRaPropertyBag.setClassPropertyNames(List.of("mRID", "normalEnabled")); + co4WithRaPropertyBag.setResourceNames(List.of("remedialAction", "contingency", "combinationConstraintKind")); + + PropertyBag co5WithRaPropertyBag = new PropertyBag(properties, true, false); + co5WithRaPropertyBag.put("mRID", "_co5xra"); + co5WithRaPropertyBag.put("remedialAction", "remedial-action"); + co5WithRaPropertyBag.put("contingency", "contingency-5"); + co5WithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + co5WithRaPropertyBag.put("normalEnabled", "true"); + co5WithRaPropertyBag.setClassPropertyNames(List.of("mRID", "normalEnabled")); + co5WithRaPropertyBag.setResourceNames(List.of("remedialAction", "contingency", "combinationConstraintKind")); + + PropertyBag co6IncludedWithRaPropertyBag = new PropertyBag(properties, true, false); + co6IncludedWithRaPropertyBag.put("mRID", "_co6xra-included"); + co6IncludedWithRaPropertyBag.put("remedialAction", "remedial-action"); + co6IncludedWithRaPropertyBag.put("contingency", "contingency-6"); + co6IncludedWithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + co6IncludedWithRaPropertyBag.put("normalEnabled", "true"); + co6IncludedWithRaPropertyBag.setClassPropertyNames(List.of("mRID", "normalEnabled")); + co6IncludedWithRaPropertyBag.setResourceNames(List.of("remedialAction", "contingency", "combinationConstraintKind")); + + PropertyBag co6ConsideredWithRaPropertyBag = new PropertyBag(List.of("mRID", "remedialAction", "contingency", "combinationConstraintKind"), true, false); + co6ConsideredWithRaPropertyBag.put("mRID", "_co6xra-considered"); + co6ConsideredWithRaPropertyBag.put("remedialAction", "remedial-action"); + co6ConsideredWithRaPropertyBag.put("contingency", "contingency-6"); + co6ConsideredWithRaPropertyBag.put("combinationConstraintKind", "http://entsoe.eu/ns/nc#ElementCombinationConstraintKind.included"); + co6ConsideredWithRaPropertyBag.setClassPropertyNames(List.of("mRID", "normalEnabled")); + co6ConsideredWithRaPropertyBag.setResourceNames(List.of("remedialAction", "contingency", "combinationConstraintKind")); + + Map contingencyStatusMap = OnContingencyStateUsageRuleHelper.processContingenciesLinkedToRemedialAction(crac, "remedial-action", Set.of(co1WithRaPropertyBag, co2WithRaPropertyBag, co3WithRaPropertyBag, co4WithRaPropertyBag, co5WithRaPropertyBag, co6IncludedWithRaPropertyBag, co6ConsideredWithRaPropertyBag)); + assertEquals( + Map.of( + "contingency-1", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.INCLUDED, ""), + "contingency-2", new AssociationStatus(true, CsaProfileConstants.ElementCombinationConstraintKind.CONSIDERED, ""), + "contingency-3", new AssociationStatus(false, null, "OnContingencyState usage rule for remedial action remedial-action with contingency contingency-3 ignored because the association is disabled."), + "contingency-4", new AssociationStatus(false, null, "OnContingencyState usage rule for remedial action remedial-action with contingency contingency-4 ignored because of an illegal combinationConstraintKind."), + "contingency-5", new AssociationStatus(false, null, "OnContingencyState usage rule for remedial action remedial-action with contingency contingency-5 ignored because this contingency does not exist or was not imported by Open RAO."), + "contingency-6", new AssociationStatus(false, null, "OnContingencyState usage rule for remedial action remedial-action with contingency contingency-6 ignored because this contingency has several conflictual links to the remedial action.") + ), + contingencyStatusMap + ); + } +} diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/RemedialActionCreationTest.java b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/RemedialActionCreationTest.java index ce86fa5485..33d6a546d2 100644 --- a/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/RemedialActionCreationTest.java +++ b/data/crac-creation/crac-creator-csa-profiles/src/test/java/com/powsybl/openrao/data/craccreation/creator/csaprofile/craccreator/remedialaction/RemedialActionCreationTest.java @@ -12,15 +12,18 @@ import java.util.List; import java.util.Optional; +import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileCracCreationTestUtil.AUTO_INSTANT_ID; import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileCracCreationTestUtil.CURATIVE_INSTANT_ID; import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileCracCreationTestUtil.NETWORK; import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileCracCreationTestUtil.PREVENTIVE_INSTANT_ID; import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileCracCreationTestUtil.assertHasOnContingencyStateUsageRule; +import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileCracCreationTestUtil.assertHasOnFlowConstraintUsageRule; import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileCracCreationTestUtil.assertHasOnInstantUsageRule; import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileCracCreationTestUtil.assertPstRangeActionImported; import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileCracCreationTestUtil.assertRaNotImported; import static com.powsybl.openrao.data.craccreation.creator.csaprofile.craccreator.CsaProfileCracCreationTestUtil.getCsaCracCreationContext; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; class RemedialActionCreationTest { @@ -29,7 +32,7 @@ void importRemedialActions() { CsaProfileCracCreationContext cracCreationContext = getCsaCracCreationContext("/profiles/remedialactions/RemedialActions.zip", NETWORK); List> importedRemedialActions = cracCreationContext.getCrac().getRemedialActions().stream().sorted(Comparator.comparing(RemedialAction::getId)).toList(); - assertEquals(6, importedRemedialActions.size()); + assertEquals(7, importedRemedialActions.size()); assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(0), "remedial-action-1", "remedial-action-1", "BBE2AA1 BBE3AA1 1", null, null, null); assertHasOnInstantUsageRule(cracCreationContext, "remedial-action-1", PREVENTIVE_INSTANT_ID, UsageMethod.AVAILABLE); @@ -52,12 +55,83 @@ void importRemedialActions() { assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-6", "contingency-1", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-6", "contingency-2", CURATIVE_INSTANT_ID, UsageMethod.FORCED); - assertEquals(5, cracCreationContext.getRemedialActionCreationContexts().stream().filter(context -> !context.isImported()).toList().size()); + assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(6), "remedial-action-9", "RTE_RA9", "BBE2AA1 BBE3AA1 1", null, null, "RTE"); + assertEquals(0, importedRemedialActions.get(6).getUsageRules().size()); + + assertEquals(4, cracCreationContext.getRemedialActionCreationContexts().stream().filter(context -> !context.isImported()).toList().size()); assertRaNotImported(cracCreationContext, "remedial-action-7", ImportStatus.NOT_FOR_RAO, "Remedial action remedial-action-7 will not be imported because normalAvailable is set to false"); assertRaNotImported(cracCreationContext, "remedial-action-8", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action remedial-action-8 will not be imported because it is linked to a contingency but is not curative"); - assertRaNotImported(cracCreationContext, "remedial-action-9", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action remedial-action-9 will not be imported because the ElementCombinationConstraintKinds that link the remedial action to the contingency contingency-1 are different"); assertRaNotImported(cracCreationContext, "remedial-action-10", ImportStatus.INCONSISTENCY_IN_DATA, "Remedial action remedial-action-10 will not be imported because of an irregular timeToImplement pattern"); assertRaNotImported(cracCreationContext, "remedial-action-11", ImportStatus.NOT_FOR_RAO, "Remedial action remedial-action-11 will not be imported because it has no elementary action"); } + + @Test + void testUsageRulesCreation() { + CsaProfileCracCreationContext cracCreationContext = getCsaCracCreationContext("/profiles/remedialactions/UsageRules.zip", NETWORK); + + List> importedRemedialActions = cracCreationContext.getCrac().getRemedialActions().stream().sorted(Comparator.comparing(RemedialAction::getId)).toList(); + assertEquals(10, importedRemedialActions.size()); + + assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(0), "remedial-action-1", "RTE_RA1", "BBE2AA1 BBE3AA1 1", null, null, "RTE"); + assertEquals(8, importedRemedialActions.get(0).getUsageRules().size()); + assertHasOnInstantUsageRule(cracCreationContext, "remedial-action-1", PREVENTIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - preventive", PREVENTIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO1 - outage - TATL 60", PREVENTIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO1 - auto - TATL 900", PREVENTIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative", PREVENTIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO2 - outage - TATL 60", PREVENTIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO2 - auto - TATL 900", PREVENTIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO2 - curative", PREVENTIVE_INSTANT_ID, UsageMethod.AVAILABLE); + + assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(2), "remedial-action-2", "RTE_RA2", "BBE2AA1 BBE3AA1 1", null, null, "RTE"); + assertEquals(3, importedRemedialActions.get(2).getUsageRules().size()); + assertHasOnInstantUsageRule(cracCreationContext, "remedial-action-2", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-2", "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-2", "RTE_AE1 (assessed-element-1) - RTE_CO2 - curative", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + + assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(3), "remedial-action-3", "RTE_RA3", "BBE2AA1 BBE3AA1 1", null, null, "RTE"); + assertEquals(4, importedRemedialActions.get(3).getUsageRules().size()); + assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-3", "contingency-1", AUTO_INSTANT_ID, UsageMethod.FORCED); + assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-3", "contingency-2", AUTO_INSTANT_ID, UsageMethod.FORCED); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-3", "RTE_AE1 (assessed-element-1) - RTE_CO1 - auto - TATL 900", AUTO_INSTANT_ID, UsageMethod.FORCED); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-3", "RTE_AE1 (assessed-element-1) - RTE_CO2 - auto - TATL 900", AUTO_INSTANT_ID, UsageMethod.FORCED); + + assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(4), "remedial-action-4", "RTE_RA4", "BBE2AA1 BBE3AA1 1", null, null, "RTE"); + assertEquals(4, importedRemedialActions.get(4).getUsageRules().size()); + assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-4", "contingency-1", CURATIVE_INSTANT_ID, UsageMethod.FORCED); + assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-4", "contingency-2", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-4", "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative", CURATIVE_INSTANT_ID, UsageMethod.FORCED); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-4", "RTE_AE1 (assessed-element-1) - RTE_CO2 - curative", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + + assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(5), "remedial-action-5", "RTE_RA5", "BBE2AA1 BBE3AA1 1", null, null, "RTE"); + assertEquals(2, importedRemedialActions.get(5).getUsageRules().size()); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-5", "RTE_AE1 (assessed-element-1) - RTE_CO2 - auto - TATL 900", AUTO_INSTANT_ID, UsageMethod.FORCED); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-5", "RTE_AE2 (assessed-element-2) - RTE_CO2 - auto - TATL 900", AUTO_INSTANT_ID, UsageMethod.FORCED); + + assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(6), "remedial-action-6", "RTE_RA6", "BBE2AA1 BBE3AA1 1", null, null, "RTE"); + assertEquals(2, importedRemedialActions.get(6).getUsageRules().size()); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-6", "RTE_AE1 (assessed-element-1) - RTE_CO2 - curative", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-6", "RTE_AE2 (assessed-element-2) - RTE_CO2 - curative", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + + assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(7), "remedial-action-7", "RTE_RA7", "BBE2AA1 BBE3AA1 1", null, null, "RTE"); + assertEquals(3, importedRemedialActions.get(7).getUsageRules().size()); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-7", "RTE_AE1 (assessed-element-1) - RTE_CO1 - auto - TATL 900", AUTO_INSTANT_ID, UsageMethod.FORCED); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-7", "RTE_AE1 (assessed-element-1) - RTE_CO2 - auto - TATL 900", AUTO_INSTANT_ID, UsageMethod.FORCED); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-7", "RTE_AE2 (assessed-element-2) - RTE_CO2 - auto - TATL 900", AUTO_INSTANT_ID, UsageMethod.FORCED); + + assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(8), "remedial-action-8", "RTE_RA8", "BBE2AA1 BBE3AA1 1", null, null, "RTE"); + assertEquals(3, importedRemedialActions.get(8).getUsageRules().size()); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-8", "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-8", "RTE_AE1 (assessed-element-1) - RTE_CO2 - curative", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-8", "RTE_AE2 (assessed-element-2) - RTE_CO2 - curative", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + + assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(9), "remedial-action-9", "RTE_RA9", "BBE2AA1 BBE3AA1 1", null, null, "RTE"); + assertEquals(2, importedRemedialActions.get(9).getUsageRules().size()); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-9", "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + assertHasOnFlowConstraintUsageRule(cracCreationContext, "remedial-action-9", "RTE_AE1 (assessed-element-1) - RTE_CO2 - curative", CURATIVE_INSTANT_ID, UsageMethod.AVAILABLE); + + assertPstRangeActionImported((PstRangeAction) importedRemedialActions.get(1), "remedial-action-10", "RTE_RA10", "BBE2AA1 BBE3AA1 1", null, null, "RTE"); + assertTrue(importedRemedialActions.get(1).getUsageRules().isEmpty()); + } } diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/resources/profiles/remedialactions/AutoRemedialActions.zip b/data/crac-creation/crac-creator-csa-profiles/src/test/resources/profiles/remedialactions/AutoRemedialActions.zip index fac5d26d63cdcc7a87ba00c8f4acfc8279d30202..63db836c602c7657600b2b4d6d706540170fd60f 100644 GIT binary patch literal 34786 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 literal 327034 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 C4S7}o diff --git a/data/crac-creation/crac-creator-csa-profiles/src/test/resources/profiles/remedialactions/UsageRules.zip b/data/crac-creation/crac-creator-csa-profiles/src/test/resources/profiles/remedialactions/UsageRules.zip new file mode 100644 index 0000000000000000000000000000000000000000..e79f007fd260840821fe10e4ddfd17e7891110f2 GIT binary patch literal 27768 zcmeHQOLN=06?U7pGdJrybC-4HrOSupIF2=r?!u#LiaX8iqAo+i4jYNo1xY(v z_9t}FO=r65rkn1%==68>2lRjs2mk>Ra^hQ2rr#FM0YTt99}W)=po`Z}e)Q!Q`S@z_ z=eNK2F5b?*+S(%DPvN!6<>+cKGC$6yM_&a;r@X&JUj2&wlPX@I7d!E#RHd$qacn# zWTM1^Is=!A%yD>2g_tIT6e*%|GmlEKSAO0-Jg|-Z{hoCmdT4rrA{YDf82ZfWQr+l7 zD80L$Z9gO3Ldq>S#E5Emt`pPi@vS07RL=EhRE%v_)*)2Pb7O>=9-b$7nhuQTT4{;3 z(>Z>+y?r$E`lrcsn%>Jc8CvIEm_SPs9Q2dK_xg7JVUXX#kD@HC3MwNl?MJbL;}a)F z$345ZXV_i%y0mSm2)=%IWC=phh(Z$uUZzKeR<2iwFj^nb;x5GA@nDqKUA#bU&tmT{ zz2U&^526Ucmp7WC8K5M+?X#1Yohv6VFi=)vbc;KKZttIYAnafJ$Q!@=BXVPNHaR;v zF6$gwYU!-q((=HWA-IXILOm_c)XTSE`oSwFLPPjIj&XvH0Px~%vNFngf)LM~=_qjC zO_8UriWgJX=10SD_Rddh`>h|ppFn?mzB3)T#9q|Z*XU_qZy3UW1vkKA3;m0+O3Eu0 z5PGwSVl*4i5q9Dbr%1vm3KQ%iYNjYa_-0{Zbd4}H1$j>F5Lz0Y4Xv*I_H<$Z2^;Ch zuR9h2DF9b8e$X&4fu0L)M&Bj=oI-bb!JVtq(@AeI=x%SrAA9K^-KXwF(G4kd%E%Tpb6(A~WVn^etIS3e z7MsXgkGtdxqgP>yVx7gXvJ(nhZ)uKLrE)i;P{Nqd_9(`VAH=Wyz*Dge@Al+v%89sK z3rq{{H1UwP!u|5}ubObgUgEW2yfnsEK~bb?elp?VhMoO_spN3um6wPEMOCY0DpjBp z-A`P67Q>D*}S*9-?=o%z`jDAd0uAQW1~BdQ9Am5LQm=2($bO&f}4l$Ig$RUT{JAzy0*DxJW)M(6Z! zo<#ARIPsXkbCnPcB(Op>%=@|H%jq;F>G1UVmIPy&R?tSsM*|h1=vBvsQPdkDwoTdc zjLE!I3aGdoWQwr5>gG%;4x{G8NhH*luW(9M+?Y1duF9$)F**|#)5h0t5We+YM5kqC zD`*YE<@(f_Xf12SaamfHs*8mYV){6b7HeD9GLWr8VWTXIESYIpmeQ5``9J^t-`+2`w#awk z%1s8XE7!VmXqvKaWAiH}16^0{8|-^z3Pgh`gEY@AGDYAc*WGkm}7KHGE;7s4hAfG@q#A* z??a|7@K-V+mjj1U^eV&}Fv_c>xeuJ>%Sfs-FGzkMju(?6 z`9`R+W_(NP7L3&Jy#qN#*o4fVId$ow<4S{QE`^25?p(ckH6n>RySwm*;C0BVDu5!n zM@g5Z$!u7lRt+z*q*4zU>7znUtt$7E_wf1luNSqt37NiHVl7Q4lzFLv3&Yq&OB!|j98b?b&7_Um{DN3M?|c9#d4993sVe;#Jcxs#Hv!?C}NHM8M+M5X7eoA zcYI-gYoFUEm-a8<*PGP85;#ZHP;nEl*80Mk%V{=~vNfzvM%gM8YA$8(vA(FpPi%c* zXIeZ!JzJf!pM4r-tJF7&vRh)!o<#Y6XehC2*ocf+RX}epu|8{wRXcu5)9&?sVMw@F z3ui8zv^)_(*_wD9c0k35gDYi+KM}NcJvo%;Ay|t7~RTOVzs4P5iYZ zqd13+xl$Pi!X76WsQNK29KFhlS%CHK1F%%7zA+gd*92fNaFP@BD*H*;qFBO$_Z#v5ZeR}BWR(yYw2aFS?mcz1lSSU6+aEWjXOG*46rnHj7HU(fb zr)L#_W#=(7T_x#A{?V{n_Mshp_u)B=O9`sJTc&j5Ax8YLWpGXxW0_XNSnPWM86UJ@ za3rxmrnC}e+~E<7lB7!B+g~0Qx{8+O6D4=D)DM36