diff --git a/ampl-export/src/main/java/eu/itesla_project/iidm/export/ampl/AmplNetworkWriter.java b/ampl-export/src/main/java/eu/itesla_project/iidm/export/ampl/AmplNetworkWriter.java index 60bcecee..1654c381 100644 --- a/ampl-export/src/main/java/eu/itesla_project/iidm/export/ampl/AmplNetworkWriter.java +++ b/ampl-export/src/main/java/eu/itesla_project/iidm/export/ampl/AmplNetworkWriter.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) - * Copyright (c) 2016, RTE (http://www.rte-france.com) + * Copyright (c) 2016-2017, 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/. @@ -1207,6 +1207,8 @@ private void writeStaticVarCompensators(AmplExportContext context) throws IOExce !append, LOCALE, new Column("num"), + new Column("fault"), + new Column(config.getActionType().getLabel()), new Column("id"))) { List skipped = new ArrayList<>(); for (StaticVarCompensator svc : network.getStaticVarCompensators()) { @@ -1214,6 +1216,8 @@ private void writeStaticVarCompensators(AmplExportContext context) throws IOExce String id = svc.getId(); int num = mapper.getInt(AmplSubset.STATIC_VAR_COMPENSATOR, id); formatter.writeCell(num) + .writeCell(faultNum) + .writeCell(actionNum) .writeCell(id); } if (skipped.size() > 0) { @@ -1398,6 +1402,8 @@ private void writeHVDCLines(AmplExportContext context) throws IOException { !append, LOCALE, new Column("num"), + new Column("fault"), + new Column(config.getActionType().getLabel()), new Column("id"))) { List skipped = new ArrayList<>(); for (HvdcLine hvdcLine : network.getHvdcLines()) { @@ -1405,6 +1411,8 @@ private void writeHVDCLines(AmplExportContext context) throws IOException { String id = hvdcLine.getId(); int num = mapper.getInt(AmplSubset.HVDC_LINE, id); formatter.writeCell(num) + .writeCell(faultNum) + .writeCell(actionNum) .writeCell(id); } if (skipped.size() > 0) { diff --git a/ampl-export/src/test/resources/lcc-test-case.txt b/ampl-export/src/test/resources/lcc-test-case.txt index 1ed3f63a..ab17e34f 100644 --- a/ampl-export/src/test/resources/lcc-test-case.txt +++ b/ampl-export/src/test/resources/lcc-test-case.txt @@ -1,3 +1,3 @@ #HVDC lines (hvdctest/InitialState) -#"num" "id" -1 "L" +#"num" "fault" "curative" "id" +1 0 0 "L" diff --git a/ampl-export/src/test/resources/svc-test-case.txt b/ampl-export/src/test/resources/svc-test-case.txt index ddbe5b1d..9e7196b9 100644 --- a/ampl-export/src/test/resources/svc-test-case.txt +++ b/ampl-export/src/test/resources/svc-test-case.txt @@ -1,3 +1,3 @@ #Static VAR compensators (svcTestCase/InitialState) -#"num" "id" -1 "SVC2" +#"num" "fault" "curative" "id" +1 0 0 "SVC2" diff --git a/ampl-export/src/test/resources/vsc-test-case.txt b/ampl-export/src/test/resources/vsc-test-case.txt index 1ed3f63a..ab17e34f 100644 --- a/ampl-export/src/test/resources/vsc-test-case.txt +++ b/ampl-export/src/test/resources/vsc-test-case.txt @@ -1,3 +1,3 @@ #HVDC lines (hvdctest/InitialState) -#"num" "id" -1 "L" +#"num" "fault" "curative" "id" +1 0 0 "L" diff --git a/iidm-actions-contingencies-xml-client/src/main/java/eu/itesla_project/iidm/actions_contingencies/xml/XmlFileContingenciesAndActionsDatabaseClient.java b/iidm-actions-contingencies-xml-client/src/main/java/eu/itesla_project/iidm/actions_contingencies/xml/XmlFileContingenciesAndActionsDatabaseClient.java index 17f283d4..7833fdaa 100644 --- a/iidm-actions-contingencies-xml-client/src/main/java/eu/itesla_project/iidm/actions_contingencies/xml/XmlFileContingenciesAndActionsDatabaseClient.java +++ b/iidm-actions-contingencies-xml-client/src/main/java/eu/itesla_project/iidm/actions_contingencies/xml/XmlFileContingenciesAndActionsDatabaseClient.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) - * Copyright (c) 2016, RTE (http://www.rte-france.com) + * Copyright (c) 2016-2017, 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/. @@ -18,7 +18,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -33,7 +32,6 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; -import eu.itesla_project.iidm.actions_contingencies.xml.mapping.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; @@ -42,8 +40,32 @@ import eu.itesla_project.contingency.ContingencyImpl; import eu.itesla_project.contingency.GeneratorContingency; import eu.itesla_project.contingency.LineContingency; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Action; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.ActionCtgAssociations; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.ActionPlan; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.ActionsContingencies; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.And; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Association; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Constraint; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Contingency; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.ElementaryAction; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Equipment; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.GenerationOperation; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.LineOperation; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.LogicalExpression; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Operand; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Or; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Parameter; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.PstOperation; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Redispatching; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.SwitchOperation; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Then; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.VoltageLevel; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Zone; +import eu.itesla_project.iidm.actions_contingencies.xml.mapping.Zones; import eu.itesla_project.iidm.network.Line; import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.iidm.network.Switch; import eu.itesla_project.iidm.network.TieLine; import eu.itesla_project.modules.contingencies.ActionElement; import eu.itesla_project.modules.contingencies.ActionPlanOption; @@ -859,51 +881,16 @@ else if (go.getAction().equals("start") LOGGER.warn("GenerationOperation : generator id not found: " + genId); } - for (SwitchOperation sw : ele.getSwitchOperation()) { - - String switchId = sw.getId(); - String vlId = null; - Zones eleZones = ele.getZones(); - if ( eleZones != null ) { - for(BigInteger zoneNum : eleZones.getNum()) { - String zoneId = zonesMapping.get(zoneNum); - eu.itesla_project.modules.contingencies.Zone z = getZone(zoneId); - Collection levels = z.getVoltageLevels(); - for (eu.itesla_project.modules.contingencies.VoltageLevel l : levels) { - String lid = l.getId(); - eu.itesla_project.iidm.network.VoltageLevel vl = network - .getVoltageLevel(lid); - if (vl != null - && vl.getBusBreakerView().getSwitch(switchId) != null) { - vlId = vl.getId(); - break; - } - } - } - } - - if (vlId == null) // search all network - { - LOGGER.info("No match found for "+switchId +" among the switches of the switch zones voltage levels. Search continues on all network switches... " ); - Iterator it = network - .getVoltageLevels().iterator(); - while (it.hasNext()) { - eu.itesla_project.iidm.network.VoltageLevel vl = it.next(); - if (vl.getBusBreakerView().getSwitch(switchId) != null) { - vlId = vl.getId(); - break; - } - - } - } - - if (vlId != null) { - if (sw.getAction().equals("opening")) - elements.add(new SwitchOpeningAction(vlId, switchId, sw.getImplementationTime(), sw.getAchievmentIndex())); - else if (sw.getAction().equals("closing")) - elements.add(new SwitchClosingAction(vlId, switchId, sw.getImplementationTime(), sw.getAchievmentIndex())); + for (SwitchOperation swOp : ele.getSwitchOperation()) { + String switchId = swOp.getId(); + Switch sw = network.getSwitch(switchId); + if (sw != null) { + if (swOp.getAction().equals("opening")) + elements.add(new SwitchOpeningAction(sw.getVoltageLevel().getId(), switchId, swOp.getImplementationTime(), swOp.getAchievmentIndex())); + else if (swOp.getAction().equals("closing")) + elements.add(new SwitchClosingAction(sw.getVoltageLevel().getId(), switchId, swOp.getImplementationTime(), swOp.getAchievmentIndex())); } else - LOGGER.warn("No match found for " + switchId + " among all network switches. The switch is eliminated from the action element list"); + LOGGER.warn("SwitchOperation : switch id not found: " + switchId); } diff --git a/iidm-actions-contingencies-xml-client/src/test/java/eu/itesla_project/iidm/actions_contingencies/xml/test/ACXmlClientTest.java b/iidm-actions-contingencies-xml-client/src/test/java/eu/itesla_project/iidm/actions_contingencies/xml/test/ACXmlClientTest.java index bf9de9c3..ca4cf744 100644 --- a/iidm-actions-contingencies-xml-client/src/test/java/eu/itesla_project/iidm/actions_contingencies/xml/test/ACXmlClientTest.java +++ b/iidm-actions-contingencies-xml-client/src/test/java/eu/itesla_project/iidm/actions_contingencies/xml/test/ACXmlClientTest.java @@ -1,42 +1,164 @@ /** - * Copyright (c) 2016, RTE (http://www.rte-france.com) + * Copyright (c) 2016-2017, 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 eu.itesla_project.iidm.actions_contingencies.xml.test; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import java.io.IOException; +import java.math.BigInteger; import java.net.URL; +import java.util.Collection; +import java.util.List; + +import javax.xml.bind.JAXBException; import org.junit.Test; +import org.xml.sax.SAXException; +import eu.itesla_project.contingency.Contingency; +import eu.itesla_project.contingency.ContingencyElement; +import eu.itesla_project.contingency.ContingencyElementType; import eu.itesla_project.iidm.actions_contingencies.xml.XmlFileContingenciesAndActionsDatabaseClient; +import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.modules.contingencies.Action; +import eu.itesla_project.modules.contingencies.ActionElement; +import eu.itesla_project.modules.contingencies.ActionElementType; +import eu.itesla_project.modules.contingencies.ActionPlan; +import eu.itesla_project.modules.contingencies.ActionPlanOption; +import eu.itesla_project.modules.contingencies.ActionsContingenciesAssociation; +import eu.itesla_project.modules.contingencies.ConstraintType; import eu.itesla_project.modules.contingencies.ContingenciesAndActionsDatabaseClient; -import org.xml.sax.SAXException; - -import javax.xml.bind.JAXBException; +import eu.itesla_project.modules.contingencies.Zone; /** + * * @author Quinary */ public class ACXmlClientTest { @Test - public void test() { - try { - URL config = getClass().getResource("/test-ac.xml"); - ContingenciesAndActionsDatabaseClient client = new XmlFileContingenciesAndActionsDatabaseClient(config); - client.getActionPlans(); - client.getZones(); - - } catch (JAXBException | SAXException | IOException e) { - e.printStackTrace(); - fail("Error " + e.getMessage()); - } + public void test() throws JAXBException, SAXException, IOException { + + URL testActionsUrl = getClass().getResource("/test-ac.xml"); + ContingenciesAndActionsDatabaseClient cadbClient = new XmlFileContingenciesAndActionsDatabaseClient(testActionsUrl); + Network network = ACXmlClientTestUtils.getNetwork(); + + // Collection getZones(); + Collection zones = cadbClient.getZones(); + assertEquals(1, zones.size()); + checkZone(zones.iterator().next(), BigInteger.ONE, "ZONE1", "zone one", 2); + // Collection getZones(Network network); + zones = cadbClient.getZones(network); + assertEquals(1, zones.size()); + checkZone(zones.iterator().next(), BigInteger.ONE, "ZONE1", "zone one", 2); + // Zone getZone(String id); + Zone zone = cadbClient.getZone("ZONE1"); + checkZone(zone, BigInteger.ONE, "ZONE1", "zone one", 2); + + // List getContingencies(Network network); + List contingencies = cadbClient.getContingencies(network); + assertEquals(1, contingencies.size()); + checkContingency(contingencies.iterator().next(), "N-1_Contingency", 1, "LINE1_ACLS", ContingencyElementType.LINE); + // Contingency getContingency(String id, Network network); + Contingency contingency = cadbClient.getContingency("N-1_Contingency", network); + checkContingency(contingency, "N-1_Contingency", 1, "LINE1_ACLS", ContingencyElementType.LINE); + + // Collection getActions(Network network); + Collection actions = cadbClient.getActions(network); + assertEquals(2, actions.size()); + checkAction(actions.iterator().next(), "Action1", 120, 1, "LINE1_ACLS", ActionElementType.LINE_TRIPPING); + // Action getAction(String id, Network network); + Action action = cadbClient.getAction("Action1", network); + checkAction(action, "Action1", 120, 1, "LINE1_ACLS", ActionElementType.LINE_TRIPPING); + + // Collection getActionPlans(); + Collection actionPlans = cadbClient.getActionPlans(); + assertEquals(1, actionPlans.size()); + checkActionPlan(actionPlans.iterator().next(), "Plan1", 1, 2, "Action1"); + // Collection getActionsCtgAssociations(); + Collection actionCtgAssociations = cadbClient.getActionsCtgAssociations(); + assertEquals(2, actionCtgAssociations.size()); + checkAssociation(actionCtgAssociations.iterator().next(), 1, "N-1_Contingency", "Action2", "LINE1_ACLS", ConstraintType.BRANCH_OVERLOAD); + // List getActionsCtgAssociations(Network network); + actionCtgAssociations = cadbClient.getActionsCtgAssociations(network); + assertEquals(2, actionCtgAssociations.size()); + checkAssociation(actionCtgAssociations.iterator().next(), 1, "N-1_Contingency", "Action2", "LINE1_ACLS", ConstraintType.BRANCH_OVERLOAD); + // Collection getActionsCtgAssociationsByContingency(String contingencyId); + actionCtgAssociations = cadbClient.getActionsCtgAssociationsByContingency("N-1_Contingency"); + assertEquals(1, actionCtgAssociations.size()); + checkAssociation(actionCtgAssociations.iterator().next(), 1, "N-1_Contingency", "Action2", "LINE1_ACLS", ConstraintType.BRANCH_OVERLOAD); + // Collection getActionsCtgAssociationsByConstraint(String equipmentId, ConstraintType constraintType); + actionCtgAssociations = cadbClient.getActionsCtgAssociationsByConstraint("LINE1_ACLS", ConstraintType.BRANCH_OVERLOAD); + assertEquals(1, actionCtgAssociations.size()); + checkAssociation(actionCtgAssociations.iterator().next(), 1, "N-1_Contingency", "Action2", "LINE1_ACLS", ConstraintType.BRANCH_OVERLOAD); + actionCtgAssociations = cadbClient.getActionsCtgAssociationsByConstraint("LINE2_ACLS", ConstraintType.BRANCH_OVERLOAD); + assertEquals(1, actionCtgAssociations.size()); + checkAssociation(actionCtgAssociations.iterator().next(), 0, null, "Action1", "LINE2_ACLS", ConstraintType.BRANCH_OVERLOAD); + } + + private void checkZone(Zone zone, BigInteger number, String name, String description, int numVoltageLevels) { + assertNotNull(zone); + assertEquals(number, zone.getNumber()); + assertEquals(name, zone.getName()); + assertEquals(description, zone.getDescription()); + assertEquals(numVoltageLevels, zone.getVoltageLevels().size()); + } + + private void checkContingency(Contingency contingency, String contingencyId, int numEquipments, String equipmentId, ContingencyElementType equipmentType) { + assertNotNull(contingency); + assertEquals(contingencyId, contingency.getId()); + assertEquals(numEquipments, contingency.getElements().size()); + ContingencyElement contingencyElement = contingency.getElements().iterator().next(); + assertEquals(equipmentId, contingencyElement.getId()); + assertEquals(equipmentType, contingencyElement.getType()); + } + + private void checkAction(Action action, String actionId, int startTime, int numEquipments, String equipmentId, ActionElementType equipmentType) { + assertNotNull(action); + assertEquals(actionId, action.getId()); + assertTrue(action.isCurative()); + assertTrue(action.isPreventive()); + assertEquals(BigInteger.valueOf(startTime), action.getStartTime()); + assertEquals(numEquipments, action.getElements().size()); + ActionElement actionElement = action.getElements().iterator().next(); + assertEquals(equipmentId, actionElement.getEquipmentId()); + assertEquals(equipmentType, actionElement.getType()); + } + + private void checkActionPlan(ActionPlan actionPlan, String name, int numOptions, int numActions, String actionId) { + assertNotNull(actionPlan); + assertEquals(name, actionPlan.getName()); + assertEquals(numOptions, actionPlan.getPriorityOption().size()); + ActionPlanOption option = actionPlan.getPriorityOption().values().iterator().next(); + assertEquals(numActions, option.getActions().size()); + assertEquals(actionId, option.getActions().values().iterator().next()); + } + private void checkAssociation(ActionsContingenciesAssociation actionCtgAssociation, int numContingencies, String contingencyId, String actionId, + String equipmentId, ConstraintType constraintType) { + assertNotNull(actionCtgAssociation); + assertEquals(numContingencies, actionCtgAssociation.getContingenciesId().size()); + if ( numContingencies > 0 ) + assertEquals(contingencyId, actionCtgAssociation.getContingenciesId().iterator().next()); + assertEquals(1, actionCtgAssociation.getActionsId().size()); + assertEquals(actionId, actionCtgAssociation.getActionsId().iterator().next()); + assertEquals(1, actionCtgAssociation.getConstraints().size()); + assertEquals(equipmentId, actionCtgAssociation.getConstraints().iterator().next().getEquipment()); + assertEquals(constraintType, actionCtgAssociation.getConstraints().iterator().next().getType()); } } diff --git a/iidm-actions-contingencies-xml-client/src/test/java/eu/itesla_project/iidm/actions_contingencies/xml/test/ACXmlClientTestUtils.java b/iidm-actions-contingencies-xml-client/src/test/java/eu/itesla_project/iidm/actions_contingencies/xml/test/ACXmlClientTestUtils.java new file mode 100644 index 00000000..9667c3c0 --- /dev/null +++ b/iidm-actions-contingencies-xml-client/src/test/java/eu/itesla_project/iidm/actions_contingencies/xml/test/ACXmlClientTestUtils.java @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.iidm.actions_contingencies.xml.test; + +import eu.itesla_project.iidm.network.Bus; +import eu.itesla_project.iidm.network.Country; +import eu.itesla_project.iidm.network.Line; +import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.iidm.network.NetworkFactory; +import eu.itesla_project.iidm.network.Substation; +import eu.itesla_project.iidm.network.TopologyKind; +import eu.itesla_project.iidm.network.VoltageLevel; + +/** + * + * @author Massimo Ferraro + */ +public class ACXmlClientTestUtils { + + public static Network getNetwork() { + Network n = NetworkFactory.create("test1", "test"); + + Substation s1 = n.newSubstation() + .setId("S1") + .setCountry(Country.FR) + .add(); + VoltageLevel vl1 = s1.newVoltageLevel() + .setId("VL1") + .setNominalV(380) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus b1 = vl1.getBusBreakerView().newBus() + .setId("B1") + .add(); + + Substation s2 = n.newSubstation() + .setId("S2") + .setCountry(Country.FR) + .add(); + VoltageLevel vl2 = s2.newVoltageLevel() + .setId("VL2") + .setNominalV(380) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus b2 = vl2.getBusBreakerView().newBus() + .setId("B2") + .add(); + + Substation s3 = n.newSubstation() + .setId("S3") + .setCountry(Country.FR) + .add(); + VoltageLevel vl3 = s3.newVoltageLevel() + .setId("VL3") + .setNominalV(380) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus b3 = vl3.getBusBreakerView().newBus() + .setId("B3") + .add(); + + Line l1 = n.newLine() + .setId("LINE1_ACLS") + .setVoltageLevel1("VL1") + .setBus1("B1") + .setConnectableBus1("B1") + .setVoltageLevel2("VL2") + .setBus2("B2") + .setConnectableBus2("B2") + .setR(0) + .setX(0) + .setG1(0) + .setG2(0) + .setB1(0) + .setB2(0) + .add(); + + Line l2 = n.newLine() + .setId("LINE2_ACLS") + .setVoltageLevel1("VL2") + .setBus1("B2") + .setConnectableBus1("B2") + .setVoltageLevel2("VL3") + .setBus2("B3") + .setConnectableBus2("B3") + .setR(0) + .setX(0) + .setG1(0) + .setG2(0) + .setB1(0) + .setB2(0) + .add(); + + return n; + } + +} diff --git a/iidm-actions-contingencies-xml-client/src/test/resources/test-ac.xml b/iidm-actions-contingencies-xml-client/src/test/resources/test-ac.xml index 5c356249..e00fac9e 100644 --- a/iidm-actions-contingencies-xml-client/src/test/resources/test-ac.xml +++ b/iidm-actions-contingencies-xml-client/src/test/resources/test-ac.xml @@ -1,6 +1,6 @@ - - - - - 1 - + + + + + 1 + - - - + + + 1 + 2 + + + + + + + + + - - - + + + + + + - + \ No newline at end of file diff --git a/modules/src/main/java/eu/itesla_project/modules/wca/WCA.java b/modules/src/main/java/eu/itesla_project/modules/wca/WCA.java index 22a6df8e..ae48edcc 100644 --- a/modules/src/main/java/eu/itesla_project/modules/wca/WCA.java +++ b/modules/src/main/java/eu/itesla_project/modules/wca/WCA.java @@ -1,5 +1,6 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) + * Copyright (c) 2017, 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/. @@ -8,6 +9,8 @@ import java.util.concurrent.CompletableFuture; +import eu.itesla_project.modules.wca.report.WCAReport; + /** * * @author Geoffroy Jamgotchian @@ -18,4 +21,6 @@ public interface WCA { CompletableFuture runAsync(String baseStateId, WCAParameters parameters) throws Exception; + WCAReport getReport(); + } diff --git a/modules/src/main/java/eu/itesla_project/modules/wca/WCAClusterNum.java b/modules/src/main/java/eu/itesla_project/modules/wca/WCAClusterNum.java index 26980cf0..3c1f5b9a 100644 --- a/modules/src/main/java/eu/itesla_project/modules/wca/WCAClusterNum.java +++ b/modules/src/main/java/eu/itesla_project/modules/wca/WCAClusterNum.java @@ -1,11 +1,16 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) + * Copyright (c) 2017, 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 eu.itesla_project.modules.wca; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + /** * @author Geoffroy Jamgotchian */ @@ -17,6 +22,11 @@ public enum WCAClusterNum { FOUR(4); private final int intValue; + + static Map mapping = new HashMap<>(); + static { + Arrays.stream(values()).forEach(clusterNum -> mapping.put(clusterNum.intValue, clusterNum)); + } WCAClusterNum(int intValue) { this.intValue = intValue; @@ -25,4 +35,11 @@ public enum WCAClusterNum { public int toIntValue() { return intValue; } + + public static WCAClusterNum fromInt(int intNum) { + if ( mapping.containsKey(intNum) ) { + return mapping.get(intNum); + } + throw new RuntimeException("Undefined cluster number " + intNum); + } } diff --git a/modules/src/main/java/eu/itesla_project/modules/wca/WCAParameters.java b/modules/src/main/java/eu/itesla_project/modules/wca/WCAParameters.java index e136391b..545ab835 100644 --- a/modules/src/main/java/eu/itesla_project/modules/wca/WCAParameters.java +++ b/modules/src/main/java/eu/itesla_project/modules/wca/WCAParameters.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) - * Copyright (c) 2016, RTE (http://www.rte-france.com) + * Copyright (c) 2016-2017, 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/. @@ -27,15 +27,11 @@ public class WCAParameters { private final double purityThreshold; - private final boolean stopWcaOnViolations; - - public WCAParameters(Interval histoInterval, String offlineWorkflowId, Set securityIndexTypes, double purityThreshold, - boolean stopWcaOnViolations) { + public WCAParameters(Interval histoInterval, String offlineWorkflowId, Set securityIndexTypes, double purityThreshold) { this.histoInterval = Objects.requireNonNull(histoInterval); this.offlineWorkflowId = offlineWorkflowId; this.securityIndexTypes = securityIndexTypes; this.purityThreshold = purityThreshold; - this.stopWcaOnViolations = stopWcaOnViolations; } public Interval getHistoInterval() { @@ -54,17 +50,12 @@ public double getPurityThreshold() { return purityThreshold; } - public boolean stopWcaOnViolations() { - return stopWcaOnViolations; - } - @Override public String toString() { return getClass().getSimpleName() + " [histoInterval=" + histoInterval + ", offlineWorkflowId=" + offlineWorkflowId + ", securityIndexTypes=" + securityIndexTypes + ", purityThreshold=" + purityThreshold + - ", stopWcaOnViolations=" + stopWcaOnViolations + "]"; } } diff --git a/modules/src/main/java/eu/itesla_project/modules/wca/WCATool.java b/modules/src/main/java/eu/itesla_project/modules/wca/WCATool.java index d40e9ba2..3c1f27f2 100644 --- a/modules/src/main/java/eu/itesla_project/modules/wca/WCATool.java +++ b/modules/src/main/java/eu/itesla_project/modules/wca/WCATool.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) - * Copyright (c) 2016, RTE (http://www.rte-france.com) + * Copyright (c) 2016-2017, 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/. @@ -8,6 +8,7 @@ package eu.itesla_project.modules.wca; import com.google.auto.service.AutoService; + import eu.itesla_project.commons.tools.Command; import eu.itesla_project.commons.tools.Tool; import eu.itesla_project.computation.ComputationManager; @@ -22,6 +23,7 @@ import eu.itesla_project.modules.online.OnlineConfig; import eu.itesla_project.modules.rules.RulesDbClient; import eu.itesla_project.simulation.securityindexes.SecurityIndexType; + import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -32,6 +34,7 @@ import org.slf4j.LoggerFactory; import java.io.BufferedWriter; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -52,13 +55,11 @@ public class WCATool implements Tool { private static final Logger LOGGER = LoggerFactory.getLogger(WCATool.class); - + private static final double DEFAULT_PURITY_THRESHOLD = 0.95; private static final char CSV_SEPARATOR = ';'; - private static final boolean DEFAULT_STOP_WCA_ON_VIOLATIONS = true; - private static Command COMMAND = new Command() { @Override @@ -116,10 +117,10 @@ public Options getOptions() { .hasArg() .argName("FILE") .build()); - options.addOption(Option.builder().longOpt("stop-on-violations") - .desc("stop WCA if there are violations, default is " + DEFAULT_STOP_WCA_ON_VIOLATIONS) + options.addOption(Option.builder().longOpt("reports-folder") + .desc("folder where to store the csv reports") .hasArg() - .argName("true/false") + .argName("FOLDER") .build()); return options; } @@ -225,13 +226,9 @@ public void run(CommandLine line) throws Exception { if (line.hasOption("output-csv-file")) { outputCsvFile = Paths.get(line.getOptionValue("output-csv-file")); } - boolean stopWcaOnViolations = DEFAULT_STOP_WCA_ON_VIOLATIONS; - if (line.hasOption("stop-on-violations")) { - stopWcaOnViolations = Boolean.parseBoolean(line.getOptionValue("stop-on-violations")); - } try (ComputationManager computationManager = new LocalComputationManager()) { - WCAParameters parameters = new WCAParameters(histoInterval, offlineWorkflowId, securityIndexTypes, purityThreshold, stopWcaOnViolations); + WCAParameters parameters = new WCAParameters(histoInterval, offlineWorkflowId, securityIndexTypes, purityThreshold); OnlineConfig config = OnlineConfig.load(); ContingenciesAndActionsDatabaseClient contingenciesDb = config.getContingencyDbClientFactoryClass().newInstance().create(); LoadFlowFactory loadFlowFactory = config.getLoadFlowFactoryClass().newInstance(); @@ -272,10 +269,10 @@ public void run(CommandLine line) throws Exception { if (cluster != null) { System.out.println("contingency " + cluster.getContingency().getId() + " done: " - + cluster.getNum() + " (" + cluster.getOrigin() + ")"); + + cluster.getNum() + " " + cluster.getOrigin()); table.addCell(cluster.getContingency().getId()); - table.addCell(cluster.getNum() + " (" + cluster.getOrigin() + ")"); + table.addCell(cluster.getNum() + " " + cluster.getOrigin()); List sortedCauses = cluster.getCauses().stream().sorted().collect(Collectors.toList()); if (sortedCauses != null && sortedCauses.size() > 0) { table.addCell(sortedCauses.get(0)); @@ -291,7 +288,12 @@ public void run(CommandLine line) throws Exception { } } } - + if ( line.hasOption("reports-folder") ) { + Path reportsFolder = Paths.get(line.getOptionValue("reports-folder")); + if ( !wca.getReport().exportCsv(reportsFolder) ) { + System.out.println("Could not store reports for network " + network.getId() + " in folder " + reportsFolder); + } + } System.out.println(table.render()); } else if (Files.isDirectory(caseFile)){ if (outputCsvFile == null) { @@ -333,6 +335,12 @@ public void run(CommandLine line) throws Exception { } clusterPerContingencyPerBaseCase.put(network.getId(), clusterPerContingency); + if ( line.hasOption("reports-folder") ) { + Path reportsFolder = Paths.get(line.getOptionValue("reports-folder") + File.separator + network.getId()); + if ( !wca.getReport().exportCsv(reportsFolder) ) { + System.out.println("Could not store reports for network " + network.getId() + " in folder " + reportsFolder); + } + } } catch (Exception e) { LOGGER.error(e.toString(), e); } diff --git a/modules/src/main/java/eu/itesla_project/modules/wca/report/WCAActionApplication.java b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCAActionApplication.java new file mode 100644 index 00000000..8fc14305 --- /dev/null +++ b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCAActionApplication.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.modules.wca.report; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import eu.itesla_project.security.LimitViolation; + +/** + * + * @author Massimo Ferraro + */ +public class WCAActionApplication { + + private final String actionId; + private final LimitViolation violation; + private final WCALoadflowResult loadflowResult; + private final boolean violationsRemoved; + private boolean actionApplied; + private final String comment; + private List postActionViolations = new ArrayList<>(); + + public WCAActionApplication(String actionId, LimitViolation violation, WCALoadflowResult loadflowResult, + boolean violationsRemoved, boolean actionApplied, String comment) { + this.actionId = Objects.requireNonNull(actionId); + this.violation = violation; + this.loadflowResult = Objects.requireNonNull(loadflowResult); + this.violationsRemoved = violationsRemoved; + this.actionApplied = actionApplied; + this.comment = comment; + } + + public String getActionId() { + return actionId; + } + + public LimitViolation getViolation() { + return violation; + } + + public WCALoadflowResult getLoadflowResult() { + return loadflowResult; + } + + public boolean areViolationsRemoved() { + return violationsRemoved; + } + + public boolean isActionApplied() { + return actionApplied; + } + + public String getComment() { + return comment; + } + + public List getPostActionViolations() { + return postActionViolations; + } + + public void setActionApplied(boolean actionApplied) { + this.actionApplied = actionApplied; + } + + public void setPostActionViolations(List postActionViolations) { + this.postActionViolations = Objects.requireNonNull(postActionViolations); + } + +} diff --git a/modules/src/main/java/eu/itesla_project/modules/wca/report/WCALoadflowResult.java b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCALoadflowResult.java new file mode 100644 index 00000000..e931741b --- /dev/null +++ b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCALoadflowResult.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.modules.wca.report; + +/** + * + * @author Massimo Ferraro + */ +public class WCALoadflowResult { + + private final boolean loadflowConverged; + private final String comment; + + public WCALoadflowResult(boolean loadflowConverged, String comment) { + this.loadflowConverged = loadflowConverged; + this.comment = comment; + } + + public boolean loadflowConverged() { + return loadflowConverged; + } + + public String getComment() { + return comment; + } + +} diff --git a/modules/src/main/java/eu/itesla_project/modules/wca/report/WCAPostContingencyStatus.java b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCAPostContingencyStatus.java new file mode 100644 index 00000000..ce56798d --- /dev/null +++ b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCAPostContingencyStatus.java @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.modules.wca.report; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import eu.itesla_project.security.LimitViolation; + +/** + * + * @author Massimo Ferraro + */ +public class WCAPostContingencyStatus { + + private final String contingencyId; + private final WCALoadflowResult postContingencyLoadflowResult; + private List postContingencyViolationsWithoutUncertainties = new ArrayList<>(); + private WCALoadflowResult postContingencyWithUncertaintiesLoadflowResult; + private List postContingencyViolationsWithUncertainties = new ArrayList<>(); + private boolean curativeActionsAvailable; + private List curativeActionsApplication = new ArrayList<>(); + + public WCAPostContingencyStatus(String contingencyId, WCALoadflowResult postContingencyLoadflowResult) { + this.contingencyId = Objects.requireNonNull(contingencyId); + this.postContingencyLoadflowResult = Objects.requireNonNull(postContingencyLoadflowResult); + } + + public String getContingencyId() { + return contingencyId; + } + + public WCALoadflowResult getPostContingencyLoadflowResult() { + return postContingencyLoadflowResult; + } + + public List getPostContingencyViolationsWithoutUncertainties() { + return postContingencyViolationsWithoutUncertainties; + } + + public void setPostContingencyViolationsWithoutUncertainties(List postContingencyViolations) { + this.postContingencyViolationsWithoutUncertainties = Objects.requireNonNull(postContingencyViolations); + } + + public WCALoadflowResult getPostContingencyWithUncertaintiesLoadflowResult() { + return postContingencyWithUncertaintiesLoadflowResult; + } + + public void setPostContingencyWithUncertaintiesLoadflowResult(WCALoadflowResult postContingencyLoadflowResult) { + this.postContingencyWithUncertaintiesLoadflowResult = Objects.requireNonNull(postContingencyLoadflowResult); + } + + public List getPostContingencyViolationsWithUncertainties() { + return postContingencyViolationsWithUncertainties; + } + + public void setPostContingencyViolationsWithUncertainties(List postContingencyViolations) { + this.postContingencyViolationsWithUncertainties = Objects.requireNonNull(postContingencyViolations); + } + + public boolean areCurativeActionsAvailable() { + return curativeActionsAvailable; + } + + public void setCurativeActionsAvailable(boolean curativeActionsAvailable) { + this.curativeActionsAvailable = curativeActionsAvailable; + } + + public List getCurativeActionsApplication() { + return curativeActionsApplication; + } + + public void setCurativeActionsApplication(List curativeActionsApplication) { + this.curativeActionsApplication = Objects.requireNonNull(curativeActionsApplication); + } + +} diff --git a/modules/src/main/java/eu/itesla_project/modules/wca/report/WCAReport.java b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCAReport.java new file mode 100644 index 00000000..4a0b473c --- /dev/null +++ b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCAReport.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.modules.wca.report; + +import java.nio.file.Path; +import java.util.List; + +import eu.itesla_project.security.LimitViolation; + +/** + * + * @author Massimo Ferraro + */ +public interface WCAReport { + + String getBasecase(); + + WCALoadflowResult getBaseStateLoadflowResult(); + + List getPreContingencyViolationsWithoutUncertainties(); + + WCALoadflowResult getBaseStateWithUncertaintiesLoadflowResult(); + + List getPreContingencyViolationsWithUncertainties(); + + List getPreventiveActionsApplication(); + + List getPostPreventiveActionsViolationsWithUncertainties(); + + List getBaseStateRemainingViolations(); + + List getSecurityRulesApplication(); + + List getPostContingenciesStatus(); + + boolean exportCsv(Path folder); + +} \ No newline at end of file diff --git a/modules/src/main/java/eu/itesla_project/modules/wca/report/WCARuleViolationType.java b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCARuleViolationType.java new file mode 100644 index 00000000..a0ee126f --- /dev/null +++ b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCARuleViolationType.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.modules.wca.report; + +/** + * + * @author Massimo Ferraro + */ +public enum WCARuleViolationType { + MISSING_RULE, + MISSING_ATTRIBUTE, + RULE_NOT_SAFE, + NO_VIOLATION +} diff --git a/modules/src/main/java/eu/itesla_project/modules/wca/report/WCASecurityRuleApplication.java b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCASecurityRuleApplication.java new file mode 100644 index 00000000..f6629998 --- /dev/null +++ b/modules/src/main/java/eu/itesla_project/modules/wca/report/WCASecurityRuleApplication.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.modules.wca.report; + +import java.util.Objects; + +import eu.itesla_project.modules.rules.SecurityRule; + +/** + * + * @author Massimo Ferraro + */ +public class WCASecurityRuleApplication { + + private final String contingencyId; + private final SecurityRule securityRule; + private final boolean isRuleViolated; + private final WCARuleViolationType ruleViolationType; + private final String cause; + + public WCASecurityRuleApplication(String contingencyId, SecurityRule securityRule, boolean isRuleViolated, + WCARuleViolationType ruleViolationType, String cause) { + this.contingencyId = Objects.requireNonNull(contingencyId); + this.securityRule = securityRule; + this.isRuleViolated = isRuleViolated; + this.ruleViolationType = Objects.requireNonNull(ruleViolationType); + this.cause = cause; + } + + public String getContingencyId() { + return contingencyId; + } + + public SecurityRule getSecurityRule() { + return securityRule; + } + + public boolean isRuleViolated() { + return isRuleViolated; + } + + public WCARuleViolationType getRuleViolationType() { + return ruleViolationType; + } + + public String getCause() { + return cause; + } + +} diff --git a/modules/src/test/java/eu/itesla_project/modules/wca/WCAClusterNumTest.java b/modules/src/test/java/eu/itesla_project/modules/wca/WCAClusterNumTest.java new file mode 100644 index 00000000..e45948c5 --- /dev/null +++ b/modules/src/test/java/eu/itesla_project/modules/wca/WCAClusterNumTest.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.modules.wca; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.junit.Test; + +/** + * + * @author Massimo Ferraro + */ +public class WCAClusterNumTest { + + @Test + public void testFromInt() { + assertEquals(WCAClusterNum.UNDEFINED, WCAClusterNum.fromInt(-1)); + assertEquals(WCAClusterNum.ONE, WCAClusterNum.fromInt(1)); + assertEquals(WCAClusterNum.FOUR, WCAClusterNum.fromInt(4)); + try { + WCAClusterNum.fromInt(-2); + fail(); + } catch(Exception ignored) { + } + } + +} diff --git a/online-workflow/src/main/java/eu/itesla_project/online/OnlineWorkflowImpl.java b/online-workflow/src/main/java/eu/itesla_project/online/OnlineWorkflowImpl.java index 7f73b5c9..8bcd5745 100644 --- a/online-workflow/src/main/java/eu/itesla_project/online/OnlineWorkflowImpl.java +++ b/online-workflow/src/main/java/eu/itesla_project/online/OnlineWorkflowImpl.java @@ -188,8 +188,7 @@ public void start(OnlineWorkflowContext oCtx) throws Exception { for (OnlineApplicationListener l : listeners) l.onWcaUpdate(new RunningSynthesis(id, true)); - // maybe we should put also the stopWcaOnViolations wca parameter as online workflow parameter - WCAParameters wcaParameters = new WCAParameters(parameters.getHistoInterval(), parameters.getOfflineWorkflowId(), parameters.getSecurityIndexes(), parameters.getRulesPurityThreshold(), true); + WCAParameters wcaParameters = new WCAParameters(parameters.getHistoInterval(), parameters.getOfflineWorkflowId(), parameters.getSecurityIndexes(), parameters.getRulesPurityThreshold()); WCA wca = wcaFactory.create(oCtx.getNetwork(), computationManager, histoDbClient, rulesDbClient, uncertaintiesAnalyserFactory, cadbClient, loadFlowFactory); WCAResult result = wca.run(wcaParameters); diff --git a/online-workflow/src/main/java/eu/itesla_project/online/modules/mock/WCAMock.java b/online-workflow/src/main/java/eu/itesla_project/online/modules/mock/WCAMock.java index 7d857d19..d0fb0448 100644 --- a/online-workflow/src/main/java/eu/itesla_project/online/modules/mock/WCAMock.java +++ b/online-workflow/src/main/java/eu/itesla_project/online/modules/mock/WCAMock.java @@ -1,31 +1,34 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) + * Copyright (c) 2016, 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 eu.itesla_project.online.modules.mock; -import eu.itesla_project.iidm.network.Network; -import eu.itesla_project.modules.contingencies.ContingenciesAndActionsDatabaseClient; -import eu.itesla_project.contingency.Contingency; -import eu.itesla_project.modules.wca.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import eu.itesla_project.contingency.Contingency; +import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.modules.contingencies.ContingenciesAndActionsDatabaseClient; +import eu.itesla_project.modules.wca.WCA; +import eu.itesla_project.modules.wca.WCAAsyncResult; +import eu.itesla_project.modules.wca.WCACluster; +import eu.itesla_project.modules.wca.WCAClusterNum; +import eu.itesla_project.modules.wca.WCAParameters; +import eu.itesla_project.modules.wca.WCAResult; +import eu.itesla_project.modules.wca.report.WCAReport; + /** * * @author Quinary */ public class WCAMock implements WCA { - private static final Logger LOGGER = LoggerFactory.getLogger(WCAMock.class); - private final WCAClusterNum clusterNum = WCAClusterNum.FOUR; // cluster 4 -> need further analysis private final Network network; @@ -40,20 +43,22 @@ public WCAMock(Network network, ContingenciesAndActionsDatabaseClient contingenc } @Override - public WCAResult run(WCAParameters parameters) throws Exception { - try { - Thread.sleep(2000); - } catch (InterruptedException e) {} + public WCAResult run(WCAParameters parameters) throws Exception { // classify all the contingencies in the same cluster - List clusters = new ArrayList<>(); + List clusters = new ArrayList<>(); for (Contingency contingency: contingenciesActionsDbClient.getContingencies(network)) { clusters.add(new WCAClusterImpl(contingency, clusterNum)); } - return new WCAResultImpl(clusters); - } + return new WCAResultImpl(clusters); + } @Override public CompletableFuture runAsync(String baseStateId, WCAParameters parameters) throws Exception { throw new UnsupportedOperationException(); } + + @Override + public WCAReport getReport() { + return null; + } } diff --git a/online-workflow/src/main/java/eu/itesla_project/online/tools/RunWcaOnStateTool.java b/online-workflow/src/main/java/eu/itesla_project/online/tools/RunWcaOnStateTool.java index 947ff70d..f8454907 100644 --- a/online-workflow/src/main/java/eu/itesla_project/online/tools/RunWcaOnStateTool.java +++ b/online-workflow/src/main/java/eu/itesla_project/online/tools/RunWcaOnStateTool.java @@ -44,8 +44,6 @@ @AutoService(Tool.class) public class RunWcaOnStateTool implements Tool { - private static final boolean DEFAULT_STOP_WCA_ON_VIOLATIONS = true; - private static Command COMMAND = new Command() { @Override @@ -98,11 +96,6 @@ public Options getOptions() { .hasArg() .argName("INDEX_TYPE,INDEX_TYPE,...") .build()); - options.addOption(Option.builder().longOpt("stop-on-violations") - .desc("stop WCA if there are violations, default is true") - .hasArg() - .argName("true/false") - .build()); return options; } @@ -144,13 +137,9 @@ public void run(CommandLine line) throws Exception { .map(SecurityIndexType::valueOf) .collect(Collectors.toSet()); } - boolean stopWcaOnViolations = DEFAULT_STOP_WCA_ON_VIOLATIONS; - if (line.hasOption("stop-on-violations")) { - stopWcaOnViolations = Boolean.parseBoolean(line.getOptionValue("stop-on-violations")); - } ComputationManager computationManager = new LocalComputationManager(); network.getStateManager().allowStateMultiThreadAccess(true); - WCAParameters wcaParameters = new WCAParameters(histoInterval, offlineWorkflowId, securityIndexTypes, purityThreshold, stopWcaOnViolations); + WCAParameters wcaParameters = new WCAParameters(histoInterval, offlineWorkflowId, securityIndexTypes, purityThreshold); ContingenciesAndActionsDatabaseClient contingenciesDb = config.getContingencyDbClientFactoryClass().newInstance().create(); LoadFlowFactory loadFlowFactory = config.getLoadFlowFactoryClass().newInstance(); try (HistoDbClient histoDbClient = config.getHistoDbClientFactoryClass().newInstance().create(); diff --git a/wca-integration/pom.xml b/wca-integration/pom.xml index 0acb2058..3adf332f 100644 --- a/wca-integration/pom.xml +++ b/wca-integration/pom.xml @@ -30,7 +30,6 @@ org.apache.servicemix.bundles org.apache.servicemix.bundles.gdata - 1.41.1_1 ${project.groupId} @@ -69,6 +68,16 @@ junit test + + org.mockito + mockito-all + test + + + org.slf4j + log4j-over-slf4j + test + org.slf4j slf4j-simple diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/SimpleContingencyDbFacade.java b/wca-integration/src/main/java/eu/itesla_project/wca/SimpleContingencyDbFacade.java index 8896c0c5..4a7c413a 100644 --- a/wca-integration/src/main/java/eu/itesla_project/wca/SimpleContingencyDbFacade.java +++ b/wca-integration/src/main/java/eu/itesla_project/wca/SimpleContingencyDbFacade.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) - * Copyright (c) 2016, RTE (http://www.rte-france.com) + * Copyright (c) 2016-2017, 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/. @@ -33,12 +33,13 @@ public class SimpleContingencyDbFacade implements ContingencyDbFacade { private final Network network; public SimpleContingencyDbFacade(ContingenciesAndActionsDatabaseClient contingenciesActionsDbClient, Network network) { - this.contingenciesActionsDbClient = contingenciesActionsDbClient; - this.network = network; + this.contingenciesActionsDbClient = Objects.requireNonNull(contingenciesActionsDbClient, "contingencies and actions db client is null"); + this.network = Objects.requireNonNull(network, "network is null"); } @Override public synchronized List getContingencies() { + LOGGER.info("Network {}: getting contingencies", network.getId()); return contingenciesActionsDbClient.getContingencies(network); } @@ -68,7 +69,7 @@ private static boolean constraintsMatch(ActionsContingenciesAssociation associat @Override public synchronized List> getCurativeActions(Contingency contingency, List limitViolations) { Objects.requireNonNull(contingency, "contingency is null"); - LOGGER.info("Getting curative actions for contingency {}", contingency.getId()); + LOGGER.info("Network {}: getting curative actions for contingency {}", network.getId(), contingency.getId()); List> curativeActions = new ArrayList<>(); for (ActionsContingenciesAssociation association : contingenciesActionsDbClient.getActionsCtgAssociations(network)) { if (!association.getContingenciesId().contains(contingency.getId())) { @@ -93,24 +94,28 @@ public synchronized List> getCurativeActions(Contingency contingenc } } } else { - LOGGER.error("Action {} not found for contingency {}", actionId , contingency.getId()); + LOGGER.error("Network {}: action {} not found for contingency {}", network.getId(), actionId , contingency.getId()); } } } } - LOGGER.info("Found {} curative actions for contingency {}", curativeActions.size(), contingency.getId()); + LOGGER.info("Network {}: found {} curative actions for contingency {}", network.getId(), curativeActions.size(), contingency.getId()); return curativeActions; } @Override public synchronized List> getPreventiveActions(LimitViolation limitViolation) { Objects.requireNonNull(limitViolation, "limit violation is null"); - LOGGER.info("Getting preventive actions for {} violation on equipment {}", limitViolation.getLimitType(), limitViolation.getSubjectId()); + LOGGER.info("Network {}: getting preventive actions for {} violation on equipment {}", + network.getId(), limitViolation.getLimitType(), limitViolation.getSubjectId()); List> preventiveActions = new ArrayList<>(); - if( !limitViolation.getLimitType().equals(LimitViolationType.CURRENT) ) // just branch overload id handled, so far + if( !limitViolation.getLimitType().equals(LimitViolationType.CURRENT) ) { // just branch overload is handled, so far + LOGGER.warn("Network {}: no preventive actions found for {} violation on equipment {}, as just branch overload is handled, so far", + network.getId(), limitViolation.getLimitType(), limitViolation.getSubjectId()); return preventiveActions; - for ( ActionsContingenciesAssociation association : contingenciesActionsDbClient.getActionsCtgAssociationsByConstraint( - limitViolation.getSubjectId(), ConstraintType.BRANCH_OVERLOAD) ) { + } + for ( ActionsContingenciesAssociation association : contingenciesActionsDbClient.getActionsCtgAssociationsByConstraint(limitViolation.getSubjectId(), + ConstraintType.BRANCH_OVERLOAD) ) { if ( !association.getContingenciesId().isEmpty() ) { // getting only actions not associated to a contingency continue; } @@ -130,12 +135,14 @@ public synchronized List> getPreventiveActions(LimitViolation limit } } } else { - LOGGER.error("Action {} not found for {} violation on equipment {}", actionId , limitViolation.getLimitType(), limitViolation.getSubjectId()); + LOGGER.error("Network {}: action {} not found for {} violation on equipment {}", + network.getId(), actionId , limitViolation.getLimitType(), limitViolation.getSubjectId()); } } } } - LOGGER.info("Found {} preventive actions for {} violation on equipment {}", preventiveActions.size(), limitViolation.getLimitType(), limitViolation.getSubjectId()); + LOGGER.info("Network {}: found {} preventive actions for {} violation on equipment {}", + network.getId(), preventiveActions.size(), limitViolation.getLimitType(), limitViolation.getSubjectId()); return preventiveActions; } } diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCAClusterImpl.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCAClusterImpl.java index 33e320ec..a35abdc1 100644 --- a/wca-integration/src/main/java/eu/itesla_project/wca/WCAClusterImpl.java +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCAClusterImpl.java @@ -1,18 +1,20 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) + * Copyright (c) 2017, 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 eu.itesla_project.wca; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; + import eu.itesla_project.contingency.Contingency; import eu.itesla_project.modules.wca.WCACluster; import eu.itesla_project.modules.wca.WCAClusterNum; -import java.util.List; -import java.util.Objects; - /** * * @author Geoffroy Jamgotchian @@ -23,14 +25,14 @@ class WCAClusterImpl implements WCACluster { private final WCAClusterNum num; - private final WCAClusterOrigin origin; + private final EnumSet origins; private final List causes; - WCAClusterImpl(Contingency contingency, WCAClusterNum num, WCAClusterOrigin origin, List causes) { + WCAClusterImpl(Contingency contingency, WCAClusterNum num, EnumSet origins, List causes) { this.contingency = Objects.requireNonNull(contingency); this.num = Objects.requireNonNull(num); - this.origin = Objects.requireNonNull(origin); + this.origins = Objects.requireNonNull(origins); this.causes = causes; } @@ -46,7 +48,7 @@ public WCAClusterNum getNum() { @Override public String getOrigin() { - return origin.toString(); + return origins.toString(); } @Override diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCAClusterOrigin.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCAClusterOrigin.java index 908f3654..b38fc67b 100644 --- a/wca-integration/src/main/java/eu/itesla_project/wca/WCAClusterOrigin.java +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCAClusterOrigin.java @@ -1,5 +1,6 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) + * Copyright (c) 2017, 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/. @@ -10,12 +11,17 @@ * @author Geoffroy Jamgotchian */ public enum WCAClusterOrigin { - HADES_BASE_DIVERGENCE, - HADES_BASE_LIMIT, - HADES_BASE_OFFLINE_RULE, - DOMAIN_LIMIT, - DOMAIN_OFFLINE_RULE, - HADES_POST_CONTINGENCY_LIMIT, - HADES_POST_CONTINGENCY_DIVERGENCE, - CLUSTER + LF_DIVERGENCE, + LF_BASIC_VIOLATION, + DOMAINS_BASIC_VIOLATION, + NO_PREVENTIVE_ACTION_FOUND, + LF_SPECIFIC_PREVENTIVE_ACTION_FOUND, + DOMAINS_SPECIFIC_PREVENTIVE_ACTION_FOUND, + DOMAINS_NO_PREVENTIVE_ACTION_FOUND, + LF_RULE_VIOLATION, + DOMAINS_RULE_VIOLATION, + LF_POST_CONTINGENCY_DIVERGENCE, + LF_POST_CONTINGENCY_VIOLATION, + LF_POST_SPECIFIC_CURATIVE_ACTION_VIOLATION, + CLUSTERS_ANALYSIS } diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCAClustersResult.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCAClustersResult.java new file mode 100644 index 00000000..b2cdd9d0 --- /dev/null +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCAClustersResult.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + +import eu.itesla_project.modules.wca.WCAClusterNum; + +/** + * + * @author Massimo Ferraro + */ +public class WCAClustersResult { + + private static final WCAClusterNum CLUSTER_NUM_DEFAULT = WCAClusterNum.UNDEFINED; + private static final boolean FOUND_VIOLATIONS_DEFAULT = false; + private static final int CURATIVE_ACTION_INDEX_DEFAULT = 0; + private static final Map INJECTIONS_DEFAULT = Collections.emptyMap(); + + private final WCAClusterNum clusterNum; + private final boolean foundViolations; + private final int curativeActionIndex; + private final Map injections; + + public WCAClustersResult() { + this(CLUSTER_NUM_DEFAULT, FOUND_VIOLATIONS_DEFAULT, CURATIVE_ACTION_INDEX_DEFAULT, INJECTIONS_DEFAULT); + } + + public WCAClustersResult(WCAClusterNum clusterNum, boolean foundViolations, + int curativeActionIndex, Map injections) { + this.clusterNum = Objects.requireNonNull(clusterNum); + this.foundViolations = foundViolations; + this.curativeActionIndex = curativeActionIndex; + this.injections = Objects.requireNonNull(injections); + } + + public WCAClusterNum getClusterNum() { + return clusterNum; + } + + public boolean foundViolations() { + return foundViolations; + } + + public int getCurativeActionIndex() { + return curativeActionIndex; + } + + public Map getInjections() { + return injections; + } + + @Override + public String toString() { + return "WCAClustersResult[" + + "clusterNum=" + clusterNum + + ",foundViolations=" + foundViolations + + ",curativeActionIndex=" + curativeActionIndex + + "]"; + } + +} diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCAConfig.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCAConfig.java index 17473d12..847e7791 100644 --- a/wca-integration/src/main/java/eu/itesla_project/wca/WCAConfig.java +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCAConfig.java @@ -7,30 +7,55 @@ */ package eu.itesla_project.wca; -import eu.itesla_project.commons.config.ModuleConfig; -import eu.itesla_project.commons.config.PlatformConfig; - import java.nio.file.Path; import java.util.EnumSet; +import java.util.HashSet; import java.util.Objects; import java.util.Set; +import eu.itesla_project.commons.config.ModuleConfig; +import eu.itesla_project.commons.config.PlatformConfig; +import eu.itesla_project.iidm.network.Country; + /** * @author Geoffroy Jamgotchian */ public class WCAConfig { - private static final float DEFAULT_REDUCED_VARIABLE_RATIO = 1f; + public static final float DEFAULT_REDUCED_VARIABLE_RATIO = 1f; + public static final boolean DEFAULT_DEBUG = false; + public static final boolean DEFAULT_EXPORT_STATE = false; + public static final Set DEFAULT_RESTRICTING_THRESHOLD_LEVELS = EnumSet.noneOf(WCARestrictingThresholdLevel.class); + public static final float DEFAULT_MARGIN = 0f; + public static final boolean DEFAULT_IGNORE_VOLTAGE_CONSTRAINTS = false; + public static final boolean DEFAULT_ACTIVATE_FILTERING = false; + public static final WCAPreventiveActionsFilter DEFAULT_PREVENTIVE_ACTIONS_FILTER = WCAPreventiveActionsFilter.LF; + public static final WCAPreventiveActionsOptimizer DEFAULT_PREVENTIVE_ACTIONS_OPTIMIZER = WCAPreventiveActionsOptimizer.DOMAINS; + public static final boolean DEFAULT_APPLY_PREVENTIVE_ACTIONS = false; + public static final WCACurativeActionsOptimizer DEFAULT_CURATIVE_ACTIONS_OPTIMIZER = WCACurativeActionsOptimizer.CLUSTERS; + public static final float DEFAULT_VOLTAGE_LEVEL_CONSTRAINTS_FILTER = 0f; + public static final Set DEFAULT_COUNTRY_CONSTRAINTS_FILTER = EnumSet.noneOf(Country.class); + public static final boolean DEFAULT_FILTER_PREVENTIVE_ACTIONS = true; + public static final boolean DEFAULT_FILTER_CURATIVE_ACTIONS = true; + public static final boolean DEFAULT_LOOOSEN_CONSTRAINTS = false; private final Path xpressHome; - private final float reducedVariableRatio; - private final boolean debug; - private final boolean exportStates; - private final Set restrictingThresholdLevels; + private final float margin; + private final boolean ignoreVoltageConstraints; + private final boolean activateFiltering; + private final WCAPreventiveActionsFilter preventiveActionsFilter; + private final WCAPreventiveActionsOptimizer preventiveActionsOptimizer; + private final boolean applyPreventiveActions; + private final WCACurativeActionsOptimizer curativeActionsOptimizer; + private final float voltageLevelConstraintFilter; + private final Set countryConstraintFilter; + private final boolean filterPreventiveActions; + private final boolean filterCurativeActions; + private final boolean loosenConstraints; public static WCAConfig load() { return load(PlatformConfig.defaultConfig()); @@ -40,18 +65,48 @@ public static WCAConfig load(PlatformConfig platformConfig) { ModuleConfig config = platformConfig.getModuleConfig("wca"); Path xpressHome = config.getPathProperty("xpressHome"); float reducedVariableRatio = config.getFloatProperty("reducedVariableRatio", DEFAULT_REDUCED_VARIABLE_RATIO); - boolean debug = config.getBooleanProperty("debug", false); - boolean exportStates = config.getBooleanProperty("exportStates", false); - Set restrictingThresholdLevels = config.getEnumSetProperty("restrictingThresholdLevels", WCARestrictingThresholdLevel.class, EnumSet.noneOf(WCARestrictingThresholdLevel.class)); - return new WCAConfig(xpressHome, reducedVariableRatio, debug, exportStates, restrictingThresholdLevels); + boolean debug = config.getBooleanProperty("debug", DEFAULT_DEBUG); + boolean exportStates = config.getBooleanProperty("exportStates", DEFAULT_EXPORT_STATE); + Set restrictingThresholdLevels = config.getEnumSetProperty("restrictingThresholdLevels", WCARestrictingThresholdLevel.class, DEFAULT_RESTRICTING_THRESHOLD_LEVELS); + float margin = Float.parseFloat(config.getStringProperty("margin", Float.toString(DEFAULT_MARGIN))); + boolean ignoreVoltageConstraints = config.getBooleanProperty("ignoreVoltageConstraints", DEFAULT_IGNORE_VOLTAGE_CONSTRAINTS); + boolean activateFiltering = config.getBooleanProperty("activateFiltering", DEFAULT_ACTIVATE_FILTERING); + WCAPreventiveActionsFilter preventiveActionsFilter = config.getEnumProperty("preventiveActionsFilter", WCAPreventiveActionsFilter.class, DEFAULT_PREVENTIVE_ACTIONS_FILTER); + WCAPreventiveActionsOptimizer preventiveActionsOptimizer = config.getEnumProperty("preventiveActionsOptimizer", WCAPreventiveActionsOptimizer.class, DEFAULT_PREVENTIVE_ACTIONS_OPTIMIZER); + boolean applyPreventiveActions = config.getBooleanProperty("applyPreventiveActions", DEFAULT_APPLY_PREVENTIVE_ACTIONS); + WCACurativeActionsOptimizer curativeActionsOptimizer = config.getEnumProperty("curativeActionsOptimizer", WCACurativeActionsOptimizer.class, DEFAULT_CURATIVE_ACTIONS_OPTIMIZER); + float voltageLevelConstraintFilter = Float.parseFloat(config.getStringProperty("voltageLevelConstraintFilter", Float.toString(DEFAULT_VOLTAGE_LEVEL_CONSTRAINTS_FILTER))); + Set countryConstraintFilter = config.getEnumSetProperty("countryConstraintFilter", Country.class, new HashSet(DEFAULT_COUNTRY_CONSTRAINTS_FILTER)); + boolean filterPreventiveActions = config.getBooleanProperty("filterPreventiveActions", DEFAULT_FILTER_PREVENTIVE_ACTIONS); + boolean filterCurativeActions = config.getBooleanProperty("filterCurativeActions", DEFAULT_FILTER_CURATIVE_ACTIONS); + boolean loosenConstraints = config.getBooleanProperty("loosenConstraints", DEFAULT_LOOOSEN_CONSTRAINTS); + return new WCAConfig(xpressHome, reducedVariableRatio, debug, exportStates, restrictingThresholdLevels, margin, ignoreVoltageConstraints, + activateFiltering, preventiveActionsFilter, preventiveActionsOptimizer, applyPreventiveActions, curativeActionsOptimizer, + voltageLevelConstraintFilter, countryConstraintFilter, filterPreventiveActions, filterCurativeActions, loosenConstraints); } - public WCAConfig(Path xpressHome, float reducedVariableRatio, boolean debug, boolean exportStates, Set restrictingThresholdLevels) { + public WCAConfig(Path xpressHome, float reducedVariableRatio, boolean debug, boolean exportStates, Set restrictingThresholdLevels, + float margin, boolean ignoreVoltageConstraints, boolean activateFiltering, WCAPreventiveActionsFilter preventiveActionsFilter, + WCAPreventiveActionsOptimizer preventiveActionsOptimizer, boolean applyPreventiveActions, WCACurativeActionsOptimizer curativeActionsOptimizer, + float voltageLevelConstraintFilter, Set countryConstraintFilter, boolean filterPreventiveActions, boolean filterCurativeActions, + boolean loosenConstraints) { this.xpressHome = Objects.requireNonNull(xpressHome); this.reducedVariableRatio = reducedVariableRatio; this.debug = debug; this.exportStates = exportStates; + this.margin = margin; this.restrictingThresholdLevels = Objects.requireNonNull(restrictingThresholdLevels, "invalid restrictingThresholdLevels"); + this.ignoreVoltageConstraints = ignoreVoltageConstraints; + this.activateFiltering = activateFiltering; + this.preventiveActionsFilter = Objects.requireNonNull(preventiveActionsFilter); + this.preventiveActionsOptimizer = Objects.requireNonNull(preventiveActionsOptimizer); + this.applyPreventiveActions = applyPreventiveActions; + this.curativeActionsOptimizer = Objects.requireNonNull(curativeActionsOptimizer); + this.voltageLevelConstraintFilter = voltageLevelConstraintFilter; + this.countryConstraintFilter = Objects.requireNonNull(countryConstraintFilter); + this.filterPreventiveActions = filterPreventiveActions; + this.filterCurativeActions = filterCurativeActions; + this.loosenConstraints = loosenConstraints; } public Path getXpressHome() { @@ -74,6 +129,54 @@ public Set getRestrictingThresholdLevels() { return restrictingThresholdLevels; } + public float getMargin() { + return margin; + } + + public boolean ignoreVoltageConstraints() { + return ignoreVoltageConstraints; + } + + public boolean activateFiltering() { + return activateFiltering; + } + + public WCAPreventiveActionsFilter getPreventiveActionsFilter() { + return preventiveActionsFilter; + } + + public WCAPreventiveActionsOptimizer getPreventiveActionsOptimizer() { + return preventiveActionsOptimizer; + } + + public boolean applyPreventiveActions() { + return applyPreventiveActions; + } + + public WCACurativeActionsOptimizer getCurativeActionsOptimizer() { + return curativeActionsOptimizer; + } + + public float getVoltageLevelConstraintFilter() { + return voltageLevelConstraintFilter; + } + + public Set getCountryConstraintFilter() { + return countryConstraintFilter; + } + + public boolean filterPreventiveActions() { + return filterPreventiveActions; + } + + public boolean filterCurativeActions() { + return filterCurativeActions; + } + + public boolean loosenConstraints() { + return loosenConstraints; + } + @Override public String toString() { return getClass().getSimpleName() + " [xpressHome=" + xpressHome + @@ -81,6 +184,18 @@ public String toString() { ", debug=" + debug + ", exportStates=" + exportStates + ", restrictingThresholdLevels=" + restrictingThresholdLevels + " -> level=" + WCARestrictingThresholdLevel.getLevel(restrictingThresholdLevels) + + ", margin=" + margin + + ", ignoreVoltageConstraints=" + ignoreVoltageConstraints + + ", activateFiltering=" + activateFiltering + + ", preventiveActionsFilter=" + preventiveActionsFilter + + ", preventiveActionsOptimizer=" + preventiveActionsOptimizer + + ", applyPreventiveActions=" + applyPreventiveActions + + ", curativeActionsOptimizer=" + curativeActionsOptimizer + + ", voltageLevelConstraintFilter=" + voltageLevelConstraintFilter + + ", countryConstraintFilter=" + countryConstraintFilter + + ", filterPreventiveActions=" + filterPreventiveActions + + ", filterCurativeActions=" + filterCurativeActions + + ", loosenConstraints=" + loosenConstraints + "]"; } } diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCACurativeActionsOptimizer.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCACurativeActionsOptimizer.java new file mode 100644 index 00000000..b1584f40 --- /dev/null +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCACurativeActionsOptimizer.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +/** + * + * @author Massimo Ferraro + */ +public enum WCACurativeActionsOptimizer { + NONE, + LF_HEURISTIC, + CLUSTERS +} diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCADomainsResult.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCADomainsResult.java new file mode 100644 index 00000000..5eb00c01 --- /dev/null +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCADomainsResult.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + +/** + * + * @author Massimo Ferraro + */ +public class WCADomainsResult { + + private static final boolean FOUND_BASIC_VIOLATIONS_DEFAULT = false; + private static final boolean RULES_VIOLATED_DEFAULT = false; + private static final int PREVENTIVE_ACTION_INDEX_DEFAULT = 0; + private static final Map INJECTIONS_DEFAULT = Collections.emptyMap(); + + private final boolean foundBasicViolations; + private final boolean rulesViolated; + private final int preventiveActionIndex; + private final Map injections; + + public WCADomainsResult() { + this(FOUND_BASIC_VIOLATIONS_DEFAULT, RULES_VIOLATED_DEFAULT, PREVENTIVE_ACTION_INDEX_DEFAULT, INJECTIONS_DEFAULT); + } + + public WCADomainsResult(boolean foundBasicViolations, boolean rulesViolated, + int preventiveActionIndex, Map injections) { + this.foundBasicViolations = foundBasicViolations; + this.rulesViolated = rulesViolated; + this.preventiveActionIndex = preventiveActionIndex; + this.injections = Objects.requireNonNull(injections); + } + + public boolean foundBasicViolations() { + return foundBasicViolations; + } + + public boolean areRulesViolated() { + return rulesViolated; + } + + public int getPreventiveActionIndex() { + return preventiveActionIndex; + } + + public Map getInjections() { + return injections; + } + + @Override + public String toString() { + return "WCADomainsResult[" + + "foundBasicViolations=" + foundBasicViolations + + ",rulesViolated=" + rulesViolated + + ",preventiveActionIndex=" + preventiveActionIndex + + "]"; + } + +} diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCAFilteredClusters.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCAFilteredClusters.java new file mode 100644 index 00000000..a9c13e37 --- /dev/null +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCAFilteredClusters.java @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +import java.util.Comparator; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import eu.itesla_project.modules.wca.WCAClusterNum; + +/** + * + * @author Massimo Ferraro + */ +public class WCAFilteredClusters { + + private static final Logger LOGGER = LoggerFactory.getLogger(WCAFilteredClusters.class); + + private final String networkId; + private Map> contingencyClusters = new ConcurrentHashMap>(); + private Map> contingencyFlags = new ConcurrentHashMap>(); + + public WCAFilteredClusters(String networkId, List contingenciesIds) { + Objects.requireNonNull(networkId,"network id is null"); + Objects.requireNonNull(contingenciesIds,"contigencies list is null"); + this.networkId = networkId; + contingenciesIds.forEach(contingencyId -> contingencyClusters.put(contingencyId, + EnumSet.of(WCAClusterNum.ONE, WCAClusterNum.TWO, WCAClusterNum.THREE, WCAClusterNum.FOUR))); + } + + public void removeClusters(String contingencyId, EnumSet clustersNums, WCAClusterOrigin flag) { + Objects.requireNonNull(contingencyId,"contingency id is null"); + Objects.requireNonNull(clustersNums,"clustersNums is null"); + LOGGER.info("Network {}, contingency {}: removing clusters {} for {}", networkId, contingencyId, clustersNums.toString(), flag); + if ( contingencyClusters.containsKey(contingencyId) ) { + // remove clusters from the list of the contingency + EnumSet clusters = contingencyClusters.get(contingencyId); + clustersNums.forEach( clusterNum -> clusters.remove(clusterNum)); + contingencyClusters.put(contingencyId, clusters); + if ( flag != null ) { + // add flag to the list of the contingency + EnumSet flags = EnumSet.noneOf(WCAClusterOrigin.class); + if ( contingencyFlags.containsKey(contingencyId) ) { + flags = contingencyFlags.get(contingencyId); + } + flags.add(flag); + contingencyFlags.put(contingencyId, flags); + } + } else { + LOGGER.warn("Network {}, contingency {}: no possible clusters", networkId, contingencyId); + } + } + + public void addClusters(String contingencyId, EnumSet clustersNums, WCAClusterOrigin flag) { + Objects.requireNonNull(contingencyId,"contingency id is null"); + Objects.requireNonNull(clustersNums,"clustersNums is null"); + LOGGER.info("Network {}, contingency {}: adding clusters {} for {}", networkId, contingencyId, clustersNums.toString(), flag); + if ( contingencyClusters.containsKey(contingencyId) ) { + // add clusters to the list of the contingency + EnumSet clusters = contingencyClusters.get(contingencyId); + clustersNums.forEach( clusterNum -> clusters.add(clusterNum)); + contingencyClusters.put(contingencyId, clusters); + if ( flag != null ) { + // add flag to the list of the contingency + EnumSet flags = EnumSet.noneOf(WCAClusterOrigin.class); + if ( contingencyFlags.containsKey(contingencyId) ) { + flags = contingencyFlags.get(contingencyId); + } + flags.add(flag); + contingencyFlags.put(contingencyId, flags); + } + } else { + LOGGER.warn("Network {}, contingency {}: no possible clusters", networkId, contingencyId); + } + } + + public boolean hasCluster(String contingencyId, WCAClusterNum clustersNum) { + Objects.requireNonNull(contingencyId,"contingency id is null"); + Objects.requireNonNull(clustersNum,"clustersNum is null"); + LOGGER.warn("Network {}, contingency {}: checking if {} is included in the possible clusters", networkId, contingencyId, clustersNum); + return contingencyClusters.containsKey(contingencyId) && contingencyClusters.get(contingencyId).contains(clustersNum); + } + + public EnumSet getClusters(String contingencyId) { + Objects.requireNonNull(contingencyId,"contingency id is null"); + LOGGER.warn("Network {}, contingency {}: getting clusters", networkId, contingencyId); + if ( contingencyClusters.containsKey(contingencyId) ) { + return contingencyClusters.get(contingencyId); + } + LOGGER.warn("Network {}, contingency {}: no possible clusters", networkId, contingencyId); + return EnumSet.noneOf(WCAClusterNum.class); + } + + public WCAClusterNum getCluster(String contingencyId) { + Objects.requireNonNull(contingencyId,"contingency id is null"); + LOGGER.warn("Network {}, contingency {}: getting cluster", networkId, contingencyId); + if ( contingencyClusters.containsKey(contingencyId) && !contingencyClusters.get(contingencyId).isEmpty() ) { + return contingencyClusters.get(contingencyId).stream().max(Comparator.naturalOrder()).get(); + } + LOGGER.warn("Network {}, contingency {}: no possible clusters", networkId, contingencyId); + return WCAClusterNum.UNDEFINED; + } + + public EnumSet getFlags(String contingencyId) { + Objects.requireNonNull(contingencyId,"contingency id is null"); + if ( contingencyFlags.containsKey(contingencyId) ) { + return contingencyFlags.get(contingencyId); + } + LOGGER.warn("Network {}, contingency {}: no available flags", networkId, contingencyId); + return EnumSet.noneOf(WCAClusterOrigin.class); + } + + public void removeAllButCluster(String contingencyId, WCAClusterNum clusterNum, WCAClusterOrigin flag) { + Objects.requireNonNull(contingencyId,"contingency id is null"); + Objects.requireNonNull(clusterNum,"clustersNums is null"); + LOGGER.info("Network {}, contigency {}: removing all clusters but {} for {}", networkId, contingencyId, clusterNum, flag); + if ( contingencyClusters.containsKey(contingencyId) ) { + if ( contingencyClusters.get(contingencyId).contains(clusterNum) ) { + contingencyClusters.put(contingencyId, EnumSet.of(clusterNum)); + if ( flag != null ) { + // add flag to the list of the contingency + EnumSet flags = EnumSet.noneOf(WCAClusterOrigin.class); + if ( contingencyFlags.containsKey(contingencyId) ) + flags = contingencyFlags.get(contingencyId); + flags.add(flag); + contingencyFlags.put(contingencyId, flags); + } + } else { + LOGGER.warn("Network {}, contingency {}: cluster {} not included in the possible clusters", networkId, contingencyId, clusterNum); + } + } else { + LOGGER.warn("Network {}, contingency {}: no possible clusters", networkId, contingencyId); + } + } + +} diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCAImpl.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCAImpl.java index 0cd46abb..c3d6fa1c 100644 --- a/wca-integration/src/main/java/eu/itesla_project/wca/WCAImpl.java +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCAImpl.java @@ -7,57 +7,92 @@ */ package eu.itesla_project.wca; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; import com.google.gdata.util.common.base.Pair; -import eu.itesla_project.commons.io.table.Column; -import eu.itesla_project.commons.io.table.TableFormatter; + import eu.itesla_project.commons.util.StringToIntMapper; -import eu.itesla_project.computation.*; +import eu.itesla_project.computation.Command; +import eu.itesla_project.computation.CommandExecution; +import eu.itesla_project.computation.ComputationManager; +import eu.itesla_project.computation.DefaultExecutionHandler; +import eu.itesla_project.computation.ExecutionEnvironment; +import eu.itesla_project.computation.ExecutionReport; +import eu.itesla_project.computation.InputFile; +import eu.itesla_project.computation.SimpleCommandBuilder; import eu.itesla_project.contingency.Contingency; import eu.itesla_project.iidm.datasource.DataSource; import eu.itesla_project.iidm.datasource.FileDataSource; -import eu.itesla_project.iidm.export.ampl.*; -import eu.itesla_project.iidm.export.ampl.util.AmplDatTableFormatter; -import eu.itesla_project.iidm.network.Identifiable; +import eu.itesla_project.iidm.export.ampl.AmplExportConfig; +import eu.itesla_project.iidm.export.ampl.AmplNetworkWriter; +import eu.itesla_project.iidm.export.ampl.AmplSubset; +import eu.itesla_project.iidm.export.ampl.AmplUtil; import eu.itesla_project.iidm.network.Network; -import eu.itesla_project.iidm.network.StateManager; import eu.itesla_project.loadflow.api.LoadFlow; import eu.itesla_project.loadflow.api.LoadFlowFactory; import eu.itesla_project.loadflow.api.LoadFlowParameters; +import eu.itesla_project.loadflow.api.LoadFlowResult; +import eu.itesla_project.modules.constraints.ConstraintsModifier; +import eu.itesla_project.modules.constraints.ConstraintsModifierConfig; import eu.itesla_project.modules.contingencies.Action; import eu.itesla_project.modules.contingencies.ContingenciesAndActionsDatabaseClient; import eu.itesla_project.modules.histo.HistoDbAttributeId; import eu.itesla_project.modules.histo.HistoDbClient; import eu.itesla_project.modules.histo.IIDM2DB; -import eu.itesla_project.modules.rules.*; -import eu.itesla_project.modules.wca.*; +import eu.itesla_project.modules.rules.RuleAttributeSet; +import eu.itesla_project.modules.rules.RuleId; +import eu.itesla_project.modules.rules.RulesDbClient; +import eu.itesla_project.modules.rules.SecurityRule; +import eu.itesla_project.modules.rules.SecurityRuleCheckReport; +import eu.itesla_project.modules.rules.SecurityRuleExpression; +import eu.itesla_project.modules.wca.Uncertainties; +import eu.itesla_project.modules.wca.UncertaintiesAnalyserFactory; +import eu.itesla_project.modules.wca.WCA; +import eu.itesla_project.modules.wca.WCAAsyncResult; +import eu.itesla_project.modules.wca.WCACluster; +import eu.itesla_project.modules.wca.WCAClusterNum; +import eu.itesla_project.modules.wca.WCAParameters; +import eu.itesla_project.modules.wca.WCAResult; +import eu.itesla_project.modules.wca.report.WCAActionApplication; +import eu.itesla_project.modules.wca.report.WCALoadflowResult; +import eu.itesla_project.modules.wca.report.WCAPostContingencyStatus; +import eu.itesla_project.modules.wca.report.WCAReport; +import eu.itesla_project.modules.wca.report.WCARuleViolationType; +import eu.itesla_project.modules.wca.report.WCASecurityRuleApplication; import eu.itesla_project.security.LimitViolation; import eu.itesla_project.security.LimitViolationFilter; import eu.itesla_project.security.LimitViolationType; import eu.itesla_project.security.Security; import eu.itesla_project.simulation.securityindexes.SecurityIndexId; import eu.itesla_project.simulation.securityindexes.SecurityIndexType; +import eu.itesla_project.wca.report.WCAReportImpl; import eu.itesla_project.wca.uncertainties.UncertaintiesAmplWriter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.zip.GZIPInputStream; /** + * * @author Geoffroy Jamgotchian */ -public class WCAImpl implements WCA, WCAConstants, AmplConstants { +public class WCAImpl implements WCA, WCAConstants { private static final Logger LOGGER = LoggerFactory.getLogger(WCAImpl.class); @@ -95,13 +130,12 @@ public class WCAImpl implements WCA, WCAConstants, AmplConstants { private static final int THREADS = 1; - private static final float UNCERTAINTY_RATIO = 1f; private static final float UNCERTAINTY_THRESHOLD = 50f; private static final int DETAILS_LEVEL_NORMAL = 2; private static final int DETAILS_LEVEL_DEBUG = 4; - - private static final Pattern CLUSTER_INDEX_PATTERN = Pattern.compile(" WCA Result : contingency_index (\\d*) contingency_cluster_index (\\d*) curative_action_index (\\d*)"); - private static final Pattern DOMAINS_RESULTS_PATTERN = Pattern.compile(" WCA Result : basic_violation (\\d*) rule_violation (\\d*) preventive_action_index (\\d*)"); + + private static final String WCA_FLOWS_FILE = "wca_flows.txt"; + private static final String WCA_UNCERTAINTIES_FILE = "wca_uncertainties.txt"; private final Network network; @@ -120,6 +154,10 @@ public class WCAImpl implements WCA, WCAConstants, AmplConstants { private final WCAConfig config; private final Map env; + + private final LimitViolationFilter violationsFilter; + + private WCAReportImpl wcaReport; public WCAImpl(Network network, ComputationManager computationManager, HistoDbClient histoDbClient, RulesDbClient rulesDbClient, UncertaintiesAnalyserFactory uncertaintiesAnalyserFactory, @@ -136,83 +174,22 @@ public WCAImpl(Network network, ComputationManager computationManager, HistoDbCl LOGGER.info(config.toString()); - env = ImmutableMap.of("XPRESS", config.getXpressHome().resolve("bin").toString(), - "LD_LIBRARY_PATH", config.getXpressHome().resolve("lib").toString()); - } + this.violationsFilter = new LimitViolationFilter().setViolationTypes(config.ignoreVoltageConstraints() ? EnumSet.of(LimitViolationType.CURRENT) : null) + .setMinBaseVoltage(config.getVoltageLevelConstraintFilter()) + .setCountries(config.getCountryConstraintFilter().isEmpty() ? null : config.getCountryConstraintFilter()); - private Matcher parseOutFile(String cmdId, Pattern pattern, Path workingDir) throws IOException { - - Path out = workingDir.resolve(cmdId + "_0.out"); - Path outGz = workingDir.resolve(cmdId + "_0.out.gz"); - if (Files.exists(out) || Files.exists(outGz)) { - try (BufferedReader reader = (Files.exists(out)) ? Files.newBufferedReader(out, StandardCharsets.UTF_8) - : new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(outGz.toFile()))))) { - String line; - while ((line = reader.readLine()) != null) { - Matcher matcher = pattern.matcher(line); - if (matcher.matches()) { - return matcher; - } - } - } - } else { - LOGGER.error("WCA output file {} or {} not found !!", out.toFile().getAbsolutePath(), outGz.toFile().getAbsolutePath()); - } - return null; - } + this.wcaReport = new WCAReportImpl(network.getId()); - private int parseClusterNum(Path workingDir) throws IOException { - int clusterNum = -1; - Matcher matcher = parseOutFile(CLUSTERS_CMD_ID, CLUSTER_INDEX_PATTERN, workingDir); - if (matcher != null) { - clusterNum = Integer.parseInt(matcher.group(2)); - } - return clusterNum; + env = ImmutableMap.of("XPRESS", config.getXpressHome().resolve("bin").toString(), + "LD_LIBRARY_PATH", config.getXpressHome().resolve("lib").toString()); } - private WCAClusterNum readClusterNum(Path workingDir) throws IOException { - WCAClusterNum clusterNum; - int clusterValue = parseClusterNum(workingDir); - switch (clusterValue) { - case 1: - clusterNum = WCAClusterNum.ONE; - break; - case 2: - clusterNum = WCAClusterNum.TWO; - break; - case 3: - clusterNum = WCAClusterNum.THREE; - break; - case 4: - clusterNum = WCAClusterNum.FOUR; - break; - case -1: - clusterNum = WCAClusterNum.UNDEFINED; - break; - default: - throw new AssertionError("Undefined cluster value" + clusterValue); - } - return clusterNum; - } - - private WCAClusterOrigin readDomainsResult(Path workingDir) throws IOException { - Matcher matcher = parseOutFile(DOMAINS_CMD_ID, DOMAINS_RESULTS_PATTERN, workingDir); - if (matcher != null) { - int basicViolation = Integer.parseInt(matcher.group(1)); - int ruleViolation = Integer.parseInt(matcher.group(2)); - if (basicViolation == 1) { - return WCAClusterOrigin.DOMAIN_LIMIT; - } else if (ruleViolation == 1) { - return WCAClusterOrigin.DOMAIN_OFFLINE_RULE; - } else { - return null; - } - } - return null; - } - - private static final AmplExportConfig DOMAINS_AMPL_EXPORT_CONFIG = new AmplExportConfig(AmplExportConfig.ExportScope.ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS_AND_ALL_LOADS, false, AmplExportConfig.ExportActionType.PREVENTIVE); - private static final AmplExportConfig CLUSTERS_AMPL_EXPORT_CONFIG = new AmplExportConfig(AmplExportConfig.ExportScope.ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS_AND_ALL_LOADS, false, AmplExportConfig.ExportActionType.CURATIVE); + private static final AmplExportConfig DOMAINS_AMPL_EXPORT_CONFIG = new AmplExportConfig(AmplExportConfig.ExportScope.ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS_AND_ALL_LOADS, + false, + AmplExportConfig.ExportActionType.PREVENTIVE); + private static final AmplExportConfig CLUSTERS_AMPL_EXPORT_CONFIG = new AmplExportConfig(AmplExportConfig.ExportScope.ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS_AND_ALL_LOADS, + false, + AmplExportConfig.ExportActionType.CURATIVE); private static final LoadFlowParameters LOAD_FLOW_PARAMETERS = new LoadFlowParameters() .setVoltageInitMode(LoadFlowParameters.VoltageInitMode.UNIFORM_VALUES) @@ -220,54 +197,12 @@ private WCAClusterOrigin readDomainsResult(Path workingDir) throws IOException { .setNoGeneratorReactiveLimits(false) .setPhaseShifterRegulationOn(false); - private static final LimitViolationFilter CURRENT_FILTER = LimitViolationFilter.load() - .setViolationTypes(EnumSet.of(LimitViolationType.CURRENT)); - private SecurityIndexType[] getSecurityIndexTypes(WCAParameters parameters) { return parameters.getSecurityIndexTypes() != null ? parameters.getSecurityIndexTypes().toArray(new SecurityIndexType[parameters.getSecurityIndexTypes().size()]) : SecurityIndexType.values(); } - private static void writeContingencies(Collection contingencies, DataSource dataSource, StringToIntMapper mapper) { - try (TableFormatter formatter = new AmplDatTableFormatter( - new OutputStreamWriter(dataSource.newOutputStream(FAULTS_FILE_SUFFIX, TXT_EXT, false), StandardCharsets.UTF_8), - "Contingencies", - INVALID_FLOAT_VALUE, - true, - LOCALE, - new Column("num"), - new Column("id"))) { - for (Contingency contingency : contingencies) { - int contingencyNum = mapper.getInt(AmplSubset.FAULT, contingency.getId()); - formatter.writeCell(contingencyNum) - .writeCell(contingency.getId()); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private static void writeActions(Collection actionIds, DataSource dataSource, StringToIntMapper mapper, - String title, AmplSubset amplSubset) { - try (TableFormatter formatter = new AmplDatTableFormatter( - new OutputStreamWriter(dataSource.newOutputStream(ACTIONS_FILE_SUFFIX, TXT_EXT, false), StandardCharsets.UTF_8), - title, - INVALID_FLOAT_VALUE, - true, - LOCALE, - new Column("num"), - new Column("id"))) { - for (String actionId : actionIds) { - int actionNum = mapper.getInt(amplSubset, actionId); - formatter.writeCell(actionNum) - .writeCell(actionId); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - private List inputFiles(int dataSetNum) { List inputFiles = new ArrayList<>(INPUT_FILE_NAMES.length + 1); inputFiles.add(new InputFile(REQUIRED_FILE_NAME)); @@ -284,14 +219,18 @@ private static void copyRequired(Path workingDir) throws IOException { Files.copy(WCAImpl.class.getResourceAsStream("/" + REQUIRED_FILE_NAME), workingDir.resolve(REQUIRED_FILE_NAME)); } - private CompletableFuture createClusterTask(Contingency contingency, List curativeActionIds, - String baseStateId, String contingencyStateId, - List curativeStateIds, List securityRuleExpressions, - Uncertainties uncertainties, WCAHistoLimits histoLimits, - StringToIntMapper mapper, - boolean stopWcaOnViolations) { + private CompletableFuture createClustersTask(Contingency contingency, + List curativeActionIds, + String baseStateId, + String contingencyStateId, + List curativeStateIds, + List securityRuleExpressions, + Uncertainties uncertainties, + WCAHistoLimits histoLimits, + StringToIntMapper mapper, + boolean activateFiltering) { return computationManager.execute(new ExecutionEnvironment(env, CLUSTERS_WORKING_DIR_PREFIX, config.isDebug()), - new DefaultExecutionHandler() { + new DefaultExecutionHandler() { @Override public List before(Path workingDir) throws IOException { @@ -328,10 +267,10 @@ public List before(Path workingDir) throws IOException { } // write contingency description - writeContingencies(Collections.singleton(contingency), dataSource, mapper); + WCAUtils.writeContingencies(Collections.singleton(contingency), dataSource, mapper); // write security rules corresponding to the contingency - new WCASecurityRulesWriter(network, securityRuleExpressions, dataSource, mapper, false, stopWcaOnViolations).write(); + new WCASecurityRulesWriter(network, securityRuleExpressions, dataSource, mapper, false, activateFiltering).write(); // write post curative state for (int i = 0; i < curativeActionIds.size(); i++) { @@ -348,7 +287,7 @@ public List before(Path workingDir) throws IOException { } // write curatives action description associated to the contingency - writeActions(curativeActionIds, dataSource, mapper, "Curative actions", AmplSubset.CURATIVE_ACTION); + WCAUtils.writeActions(curativeActionIds, dataSource, mapper, "Curative actions", AmplSubset.CURATIVE_ACTION); Command cmd = new SimpleCommandBuilder() .id(CLUSTERS_CMD_ID) @@ -362,30 +301,34 @@ public List before(Path workingDir) throws IOException { Float.toString(config.getReducedVariableRatio()), Float.toString(UNCERTAINTY_THRESHOLD), Integer.toString(config.isDebug() ? DETAILS_LEVEL_DEBUG : DETAILS_LEVEL_NORMAL), - Integer.toString(WCARestrictingThresholdLevel.getLevel(config.getRestrictingThresholdLevels()))) + Integer.toString(WCARestrictingThresholdLevel.getLevel(config.getRestrictingThresholdLevels())), + WCA_FLOWS_FILE, + WCA_UNCERTAINTIES_FILE) .build(); return Arrays.asList(new CommandExecution(cmd, 1)); } @Override - public WCACluster after(Path workingDir, ExecutionReport report) throws IOException { + public WCAClustersResult after(Path workingDir, ExecutionReport report) throws IOException { report.log(); - WCAClusterNum clusterNum = readClusterNum(workingDir); - return new WCAClusterImpl(contingency, clusterNum, WCAClusterOrigin.CLUSTER, - Collections.singletonList("Actions tested by clusters: " + curativeActionIds)); + WCAClustersResult clustersResult = WCAUtils.readClustersResult(CLUSTERS_CMD_ID, workingDir, WCA_FLOWS_FILE, WCA_UNCERTAINTIES_FILE); + LOGGER.info("Network {}, contingency {}: 'clusters' result = {}", network.getId(), contingency.getId(), clustersResult.toString()); + return clustersResult; } }); } - private CompletableFuture createDomainTask(Contingency contingency, String baseStateId, - List securityRuleExpressions, - Uncertainties uncertainties, WCAHistoLimits histoLimits, - StringToIntMapper mapper, - List preventiveStateIds, - List preventiveActionIds, - boolean stopWcaOnViolations) { + private CompletableFuture createDomainsTask(Contingency contingency, + String baseStateId, + List securityRuleExpressions, + Uncertainties uncertainties, + WCAHistoLimits histoLimits, + StringToIntMapper mapper, + List preventiveStateIds, + List preventiveActionIds, + boolean activateFiltering) { return computationManager.execute(new ExecutionEnvironment(env, DOMAINS_WORKING_DIR_PREFIX, config.isDebug()), - new DefaultExecutionHandler() { + new DefaultExecutionHandler() { @Override public List before(Path workingDir) throws IOException { @@ -402,7 +345,7 @@ public List before(Path workingDir) throws IOException { // write historical interval histoLimits.write(commonDataSource, mapper); - int contingencyNum = mapper.newInt(AmplSubset.FAULT, contingency.getId()); + int contingencyNum = contingency == null ? 1 : mapper.newInt(AmplSubset.FAULT, contingency.getId()); int dataSetNum = contingencyNum - 1; DataSource dataSource = new FileDataSource(workingDir, FILE_PREFIX + dataSetNum); @@ -414,7 +357,7 @@ public List before(Path workingDir) throws IOException { } // write contingency description - writeContingencies(Collections.singleton(contingency), dataSource, mapper); + WCAUtils.writeContingencies(contingency == null ? Collections.emptyList() : Collections.singleton(contingency), dataSource, mapper); // write post preventive state for (int i = 0; i < preventiveActionIds.size(); i++) { @@ -431,10 +374,10 @@ public List before(Path workingDir) throws IOException { } // write preventive action description - writeActions(preventiveActionIds, dataSource, mapper, "Preventive actions", AmplSubset.PREVENTIVE_ACTION); + WCAUtils.writeActions(preventiveActionIds, dataSource, mapper, "Preventive actions", AmplSubset.PREVENTIVE_ACTION); // write security rules corresponding to the contingency - new WCASecurityRulesWriter(network, securityRuleExpressions, dataSource, mapper, false, stopWcaOnViolations).write(); + new WCASecurityRulesWriter(network, securityRuleExpressions, dataSource, mapper, false, activateFiltering).write(); Command cmd = new SimpleCommandBuilder() .id(DOMAINS_CMD_ID) @@ -449,150 +392,267 @@ public List before(Path workingDir) throws IOException { Float.toString(UNCERTAINTY_THRESHOLD), Integer.toString(SecurityIndexType.TSO_OVERLOAD.ordinal()), Integer.toString(config.isDebug() ? DETAILS_LEVEL_DEBUG : DETAILS_LEVEL_NORMAL), - Integer.toString(WCARestrictingThresholdLevel.getLevel(config.getRestrictingThresholdLevels()))) + Integer.toString(WCARestrictingThresholdLevel.getLevel(config.getRestrictingThresholdLevels())), + WCA_FLOWS_FILE, + WCA_UNCERTAINTIES_FILE) .build(); return Arrays.asList(new CommandExecution(cmd, 1)); } @Override - public WCACluster after(Path workingDir, ExecutionReport report) throws IOException { + public WCADomainsResult after(Path workingDir, ExecutionReport report) throws IOException { report.log(); - WCAClusterOrigin origin = readDomainsResult(workingDir); - if (origin != null) { - return new WCAClusterImpl(contingency, WCAClusterNum.FOUR, origin, Collections.emptyList()); - } - return null; + WCADomainsResult domainsResult = WCAUtils.readDomainsResult(DOMAINS_CMD_ID, workingDir, WCA_UNCERTAINTIES_FILE); + LOGGER.info("Network {}, {}: 'domains' result = {}", network.getId(), contingency == null ? "pre-contingency" : "contingency " + contingency.getId(), domainsResult.toString()); + return domainsResult; } }); } - private CompletableFuture createClusterTaskWithDeps(Contingency contingency, List curativeActionIds, - String baseStateId, String contingencyStateId, - List curativeStateIds, List securityRuleExpressions, - Supplier> memoizedUncertaintiesFuture, - Supplier> histoLimitsFuture, - StringToIntMapper mapper, - boolean stopWcaOnViolations) { + private CompletableFuture createClustersTaskWithDeps(Contingency contingency, + List curativeActionIds, + String baseStateId, + String contingencyStateId, + List curativeStateIds, + List securityRuleExpressions, + Supplier> memoizedUncertaintiesFuture, + Supplier> histoLimitsFuture, + StringToIntMapper mapper, + boolean activateFiltering) { return memoizedUncertaintiesFuture.get() .thenCombine(histoLimitsFuture.get(), (uncertainties, histoLimits) -> Pair.of(uncertainties, histoLimits)) - .thenCompose(p -> createClusterTask(contingency, curativeActionIds, baseStateId, contingencyStateId, - curativeStateIds, securityRuleExpressions, p.getFirst(), p.getSecond(), mapper, stopWcaOnViolations)); + .thenCompose(p -> createClustersTask(contingency, curativeActionIds, baseStateId, contingencyStateId, curativeStateIds, + securityRuleExpressions, p.getFirst(), p.getSecond(), mapper, activateFiltering)); } - private CompletableFuture createDomainTaskWithDeps(Contingency contingency, String baseStateId, - List securityRuleExpressions, - Supplier> memoizedUncertaintiesFuture, - Supplier> histoLimitsFuture, - StringToIntMapper mapper, - List preventiveStateIds, - List preventiveActionIds, - boolean stopWcaOnViolations) { + private CompletableFuture createDomainsTaskWithDeps(Contingency contingency, + String baseStateId, + List securityRuleExpressions, + Supplier> memoizedUncertaintiesFuture, + Supplier> histoLimitsFuture, + StringToIntMapper mapper, + List preventiveStateIds, + List preventiveActionIds, + boolean activateFiltering) { return memoizedUncertaintiesFuture.get() .thenCombine(histoLimitsFuture.get(), (uncertainties, histoLimits) -> Pair.of(uncertainties, histoLimits)) - .thenCompose(p -> createDomainTask(contingency, baseStateId, securityRuleExpressions, p.getFirst(), p.getSecond(), mapper, - preventiveStateIds, preventiveActionIds, stopWcaOnViolations)); + .thenCompose(p -> createDomainsTask(contingency, baseStateId, securityRuleExpressions, p.getFirst(), p.getSecond(), mapper, + preventiveStateIds, preventiveActionIds, activateFiltering)); } - private CompletableFuture createClusterWorkflowTask(Contingency contingency, String baseStateId, - ContingencyDbFacade contingencyDbFacade, - List securityRuleExpressions, - Supplier> memoizedUncertaintiesFuture, - Supplier> histoLimitsFuture, - StringToIntMapper mapper, - LoadFlow loadFlow, - boolean stopWcaOnViolations) { + private CompletableFuture createClustersWorkflowTask(Contingency contingency, String baseStateId, + ContingencyDbFacade contingencyDbFacade, + List securityRuleExpressions, + Supplier> memoizedUncertaintiesFuture, + Supplier> histoLimitsFuture, + StringToIntMapper mapper, + LoadFlow loadFlow, + boolean activateFiltering, + boolean filterCurativeActions, + WCAFilteredClusters filteredClusters) { String[] contingencyStateId = new String[1]; List curativeStateIds = Collections.synchronizedList(new ArrayList<>()); List curativeActionIds = Collections.synchronizedList(new ArrayList<>()); return CompletableFuture .runAsync(() -> { - // create post contingency state - contingencyStateId[0] = baseStateId + "_" + contingency.getId(); - network.getStateManager().cloneState(baseStateId, contingencyStateId[0]); - network.getStateManager().setWorkingState(contingencyStateId[0]); + LOGGER.info("Network {}, contingency {}: computing post contingency state", network.getId(), contingency.getId()); + contingencyStateId[0] = baseStateId + "_" + contingency.getId(); + network.getStateManager().cloneState(baseStateId, contingencyStateId[0]); + network.getStateManager().setWorkingState(contingencyStateId[0]); - // apply contingency to the network - contingency.toTask().modify(network); + contingency.toTask().modify(network, computationManager); - }, computationManager.getExecutor()) + }, computationManager.getExecutor()) .thenCompose(aVoid -> loadFlow.runAsync(contingencyStateId[0], LOAD_FLOW_PARAMETERS)) .thenCompose(loadFlowResult -> { if (!loadFlowResult.isOk()) { - return CompletableFuture.completedFuture(new WCAClusterImpl(contingency, - WCAClusterNum.FOUR, - WCAClusterOrigin.HADES_POST_CONTINGENCY_DIVERGENCE, - Collections.singletonList(contingency.getId()))); + LOGGER.warn("Network {}, contingency {}: load flow on post contingency state diverged, metrics = {}", + network.getId(), contingency.getId(), loadFlowResult.getMetrics()); + filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE,WCAClusterNum.TWO,WCAClusterNum.THREE), + WCAClusterOrigin.LF_POST_CONTINGENCY_DIVERGENCE); + wcaReport.addPostContingencyStatus(new WCAPostContingencyStatus( + contingency.getId(), + new WCALoadflowResult(false, "load flow on post contingency state diverged: metrics = " + loadFlowResult.getMetrics()) + )); + return CompletableFuture.completedFuture(WCAClusterNum.FOUR); } else { network.getStateManager().setWorkingState(contingencyStateId[0]); - List contingencyStateLimitViolations = CURRENT_FILTER.apply(Security.checkLimits(network)); + WCAPostContingencyStatus postContingencyStatus = new WCAPostContingencyStatus(contingency.getId(), new WCALoadflowResult(true, null)); - List> curativeActions = contingencyDbFacade.getCurativeActions(contingency, null); // TODO pass post contingency violations? + List contingencyStateLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + if ( contingencyStateLimitViolations.size() > 0 ) { + LOGGER.warn("Network {}, contingency {}: constraint violantions found in post contingency state:\n{}", + network.getId(), contingency.getId(), Security.printLimitsViolations(contingencyStateLimitViolations, violationsFilter)); + postContingencyStatus.setPostContingencyViolationsWithoutUncertainties(contingencyStateLimitViolations); + filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE), + WCAClusterOrigin.LF_POST_CONTINGENCY_VIOLATION); + } - if (curativeActions.isEmpty()) { - // check limits on contingency state - if (contingencyStateLimitViolations.size() > 0 && stopWcaOnViolations) { - return CompletableFuture.completedFuture(new WCAClusterImpl(contingency, - WCAClusterNum.FOUR, - WCAClusterOrigin.HADES_POST_CONTINGENCY_LIMIT, - Collections.singletonList(contingency.getId()))); + if ( WCACurativeActionsOptimizer.CLUSTERS.equals(config.getCurativeActionsOptimizer()) + || WCACurativeActionsOptimizer.LF_HEURISTIC.equals(config.getCurativeActionsOptimizer()) ) { + LOGGER.info("Network {}, contingency {}: getting curative actions", network.getId(), contingency.getId()); + List> curativeActions = contingencyDbFacade.getCurativeActions(contingency, null); // pass post contingency violations? + LOGGER.info("Network {}, contingency {}: found {} curative actions", network.getId(), contingency.getId(), curativeActions.size()); + if (curativeActions.isEmpty()) { + LOGGER.warn("Network {}, contingency {}: found no curative actions", network.getId(), contingency.getId()); + filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE), + WCAClusterOrigin.LF_POST_SPECIFIC_CURATIVE_ACTION_VIOLATION); + postContingencyStatus.setCurativeActionsAvailable(false); + wcaReport.addPostContingencyStatus(postContingencyStatus); + if ( WCACurativeActionsOptimizer.CLUSTERS.equals(config.getCurativeActionsOptimizer()) ) { + return createClustersTaskWithDeps(contingency, Collections.emptyList(), baseStateId, contingencyStateId[0], + Collections.emptyList(), securityRuleExpressions, memoizedUncertaintiesFuture, + histoLimitsFuture, mapper, activateFiltering) + .thenCompose(clusterResults -> { + return CompletableFuture.completedFuture(clusterResults.getClusterNum()); + }); + } else { + return CompletableFuture.completedFuture(WCAClusterNum.UNDEFINED); + } } else { - return createClusterTaskWithDeps(contingency, Collections.emptyList(), baseStateId, contingencyStateId[0], - Collections.emptyList(), securityRuleExpressions, memoizedUncertaintiesFuture, - histoLimitsFuture, mapper, stopWcaOnViolations); - } - } else { - CompletableFuture[] curativeActionTasks = new CompletableFuture[curativeActions.size()]; - List curativeStateIdsForClusters = Collections.synchronizedList(new ArrayList<>()); - List curativeActionIdsForClusters = Collections.synchronizedList(new ArrayList<>()); - - // create post curative states - for (int i = 0; i < curativeActions.size(); i++) { - List curativeAction = curativeActions.get(i); - - String curativeActionId = curativeAction.stream().map(Action::getId).collect(Collectors.joining("+")); - curativeActionIds.add(curativeActionId); - String curativeStateId = contingencyStateId[0] + "_" + curativeActionId; - curativeStateIds.add(curativeStateId); - - curativeActionTasks[i] = CompletableFuture.runAsync(() -> { - network.getStateManager().cloneState(contingencyStateId[0], curativeStateId); + List curativeStateIdsForClusters = Collections.synchronizedList(new ArrayList<>()); + List curativeActionIdsForClusters = Collections.synchronizedList(new ArrayList<>()); + List curativeActionsApplication = Collections.synchronizedList(new ArrayList<>()); + curativeActions.sort((o1, o2) -> o1.stream().map(Action::getId).collect(Collectors.joining("+")).compareTo(o2.stream().map(Action::getId).collect(Collectors.joining("+")))); + String previousState = contingencyStateId[0]; + for (int i = 0; i < curativeActions.size(); i++) { + List curativeAction = curativeActions.get(i); + String curativeActionId = curativeAction.stream().map(Action::getId).collect(Collectors.joining("+")); + curativeActionIds.add(curativeActionId); + String curativeStateId = previousState + "_" + curativeActionId; + curativeStateIds.add(curativeStateId); + LOGGER.info("Network {}, contingency {}, curative action {}: starting analysis", + network.getId(), contingency.getId(), curativeActionId); + network.getStateManager().cloneState(previousState, curativeStateId); network.getStateManager().setWorkingState(curativeStateId); - - // apply curative actions to the network + LOGGER.info("Network {}, contingency {}, curative action {}: computing post curative action state", + network.getId(), contingency.getId(), curativeActionId); for (Action subAction : curativeAction) { - subAction.toTask().modify(network); + subAction.toTask().modify(network, computationManager); } - }, computationManager.getExecutor()) - .thenCompose(ignored -> loadFlow.runAsync(curativeStateId, LOAD_FLOW_PARAMETERS)) - .thenAccept(loadFlowResult1 -> { - if (loadFlowResult1.isOk()) { - network.getStateManager().setWorkingState(curativeStateId); - - List curativeStateLimitViolations = CURRENT_FILTER.apply(Security.checkLimits(network)); - if (curativeStateLimitViolations.isEmpty() || !stopWcaOnViolations) { + LoadFlowResult loadFlowResult1; + try { + loadFlowResult1 = loadFlow.run(LOAD_FLOW_PARAMETERS); + if (loadFlowResult1.isOk()) { + boolean violationsRemoved = false; + boolean actionApplied = false; + String comment = null; + List curativeStateLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + if (curativeStateLimitViolations.isEmpty()) { + LOGGER.info("Network {}, contingency {}, curative action {} solves violations: adding curative action to list for 'clusters' task", + network.getId(), contingency.getId(), curativeActionId); + curativeStateIdsForClusters.add(curativeStateId); + curativeActionIdsForClusters.add(curativeActionId); + violationsRemoved = true; + actionApplied = true; + previousState = curativeStateId; + } else { + LOGGER.warn("Network {}, contingency {}, curative action {}: violantions found in post curative action state:\n{}", + network.getId(), contingency.getId(), curativeActionId, Security.printLimitsViolations(curativeStateLimitViolations, violationsFilter)); + comment = "violantions found in post curative action state"; + if ( !filterCurativeActions ) { + LOGGER.info("Network {}, contingency {}, curative action {}: adding anyway curative action to list for 'clusters' task (config filterCurativeActions = false)", + network.getId(), contingency.getId(), curativeActionId); curativeStateIdsForClusters.add(curativeStateId); curativeActionIdsForClusters.add(curativeActionId); + actionApplied = true; + previousState = curativeStateId; } } + curativeActionsApplication.add(new WCAActionApplication(curativeActionId, + null, + new WCALoadflowResult(true, null), + violationsRemoved, + actionApplied, + comment)); + } else { + LOGGER.warn("Network {}, contingency {}, curative action {}: load flow on post curative action state diverged, metrics = {}", + network.getId(), contingency.getId(), curativeActionId, loadFlowResult1.getMetrics()); + curativeActionsApplication.add(new WCAActionApplication(curativeActionId, + null, + new WCALoadflowResult(false, "load flow on post curative action state diverged: metrics = " + loadFlowResult1.getMetrics()), + false, + false, + null)); + } + } catch (Exception e) { + LOGGER.warn("Network {}, contingency {}, curative action {}: load flow on post curative action state failed: {}", + network.getId(), contingency.getId(), curativeActionId, e.getMessage(), e); + curativeActionsApplication.add(new WCAActionApplication(curativeActionId, + null, + new WCALoadflowResult(false, "load flow on post curative action state failed: " + e.getMessage()), + false, + false, + null)); + } + } + postContingencyStatus.setCurativeActionsApplication(curativeActionsApplication); + if ( curativeActionIdsForClusters.isEmpty() ) { + LOGGER.warn("Network {}, contingency {}: no available curative actions", network.getId(), contingency.getId()); + filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE), + WCAClusterOrigin.LF_POST_SPECIFIC_CURATIVE_ACTION_VIOLATION); + } + return CompletableFuture.completedFuture(new WCAClustersResult()) + .thenCompose(clusterResults -> { + if ( WCACurativeActionsOptimizer.CLUSTERS.equals(config.getCurativeActionsOptimizer()) ) { + LOGGER.info("Network {}, contingency {}: running 'clusters' curative action optimizer", network.getId(), contingency.getId()); + return createClustersTaskWithDeps(contingency, curativeActionIdsForClusters, baseStateId, contingencyStateId[0], + curativeStateIdsForClusters, securityRuleExpressions, memoizedUncertaintiesFuture, + histoLimitsFuture, mapper, activateFiltering); + } else { + return CompletableFuture.completedFuture(clusterResults); + } }) - .exceptionally(throwable -> { - if (throwable != null) { - LOGGER.error(throwable.toString(), throwable); + .thenCompose(clusterResults2 -> { + if ( clusterResults2.foundViolations() ) { + LOGGER.info("Network {}, contingency {}: 'clusters' found violations", network.getId(), contingency.getId()); + String clustersUncertaintiesState = contingencyStateId[0] + "_clustersUncertaintiesState"; + LOGGER.info("Network {}, contingency {}: creating post contingency state with 'clusters' uncertainties", + network.getId(), contingency.getId()); + network.getStateManager().cloneState(contingencyStateId[0], clustersUncertaintiesState); + network.getStateManager().setWorkingState(clustersUncertaintiesState); + WCAUtils.applyInjections(network, clustersUncertaintiesState, clusterResults2.getInjections()); + LOGGER.info("Network {}, contingency {}: running loadflow on post contingency state with 'clusters' uncertainties", + network.getId(), contingency.getId()); + loadFlow.runAsync(clustersUncertaintiesState, LOAD_FLOW_PARAMETERS) + .thenAccept(loadFlowResult2 -> { + if (!loadFlowResult2.isOk()) { + LOGGER.info("Network {}, contingency {}: loadflow on state with 'clusters' uncertainties diverged: metrics = {}", + network.getId(), contingency.getId(), loadFlowResult.getMetrics()); + postContingencyStatus.setPostContingencyWithUncertaintiesLoadflowResult( + new WCALoadflowResult(false, + "load flow on post contingency state with 'clusters' uncertainties diverged: metrics = " + loadFlowResult.getMetrics()) + ); + } else { + postContingencyStatus.setPostContingencyWithUncertaintiesLoadflowResult(new WCALoadflowResult(true, null)); + List clustersLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + if ( clustersLimitViolations.size() > 0 ) { + LOGGER.warn("Network {}, contingency {}: constraint violantions found in state with 'clusters' uncertainties:\n{}", + network.getId(), contingency.getId(), Security.printLimitsViolations(clustersLimitViolations, violationsFilter)); + postContingencyStatus.setPostContingencyViolationsWithUncertainties(clustersLimitViolations); + } else { + LOGGER.warn("Network {}, contingency {}: no violations found in state with 'clusters' uncertainties", + network.getId(), contingency.getId()); + } + } + }).join(); } - return null; + wcaReport.addPostContingencyStatus(postContingencyStatus); + return CompletableFuture.completedFuture(clusterResults2.getClusterNum()); }); } - - return CompletableFuture.allOf(curativeActionTasks) - .thenComposeAsync(aVoid -> createClusterTaskWithDeps(contingency, curativeActionIds, baseStateId, contingencyStateId[0], - curativeStateIds, securityRuleExpressions, memoizedUncertaintiesFuture, - histoLimitsFuture, mapper, stopWcaOnViolations)); + } else { + return CompletableFuture.completedFuture(WCAClusterNum.UNDEFINED); } } }) - .handle((cluster, throwable) -> { + .handle((clusterNumber, throwable) -> { if (throwable != null) { LOGGER.error(throwable.toString(), throwable); } @@ -602,7 +662,7 @@ private CompletableFuture createClusterWorkflowTask(Contingency cont for (String curativeStateId : curativeStateIds) { network.getStateManager().removeState(curativeStateId); } - return cluster; + return clusterNumber; }); } @@ -613,7 +673,6 @@ private CompletableFuture>> createWcaTask(Str LoadFlow loadFlow = loadFlowFactory.create(network, computationManager, 0); - // run the LF on N state return loadFlow .runAsync(baseStateId, LOAD_FLOW_PARAMETERS) .thenApply(loadFlowInBaseStateResult -> { @@ -625,174 +684,434 @@ private CompletableFuture>> createWcaTask(Str StringToIntMapper mapper = AmplUtil.createMapper(network); Collection contingencies = contingencyDbFacade.getContingencies(); + LOGGER.info("Network {}: working on {} contingencies", network.getId(), contingencies.size()); List> clusters = new ArrayList<>(contingencies.size()); + + WCAFilteredClusters filteredClusters = new WCAFilteredClusters(network.getId(), contingencies.stream().map(Contingency::getId).collect(Collectors.toList())); - // check base load flow divergence if (!loadFlowInBaseStateResult.isOk()) { - for (Contingency contingency : contingencies) { + LOGGER.error("Network {}: load flow on base state diverged, metrics = {}", network.getId(), loadFlowInBaseStateResult.getMetrics()); + wcaReport.setBaseStateLoadflowResult(new WCALoadflowResult(false,"load flow on base state diverged: metrics = " + loadFlowInBaseStateResult.getMetrics())); + contingencies.forEach(contingency -> { + filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE,WCAClusterNum.TWO,WCAClusterNum.THREE), + WCAClusterOrigin.LF_DIVERGENCE); clusters.add(CompletableFuture.completedFuture(new WCAClusterImpl(contingency, - WCAClusterNum.FOUR, - WCAClusterOrigin.HADES_BASE_DIVERGENCE, - Collections.emptyList()))); - } + WCAClusterNum.FOUR, + EnumSet.of(WCAClusterOrigin.LF_DIVERGENCE), + Collections.emptyList()))); + }); } else { - // check limits on base state - List baseStateLimitViolations = CURRENT_FILTER.apply(Security.checkLimits(network)); - if (baseStateLimitViolations.size() > 0 && parameters.stopWcaOnViolations()) { - for (Contingency contingency : contingencies) { - clusters.add(CompletableFuture.completedFuture(new WCAClusterImpl(contingency, - WCAClusterNum.FOUR, - WCAClusterOrigin.HADES_BASE_LIMIT, - baseStateLimitViolations.stream().map(LimitViolation::getSubjectId) - .distinct() - .collect(Collectors.toList())))); - } - } else { - // commons tasks to all contingency task, a supplier is used to cache to completable future - // result - Supplier> uncertainties = Suppliers.memoize(() -> { - network.getStateManager().setWorkingState(baseStateId); - try { - return uncertaintiesAnalyserFactory.create(network, histoDbClient, computationManager) - .analyse(parameters.getHistoInterval()); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - - Supplier> histoLimits - = Suppliers.memoize(() -> CompletableFuture.supplyAsync(() -> { - network.getStateManager().setWorkingState(baseStateId); - try { - WCAHistoLimits limits = new WCAHistoLimits(parameters.getHistoInterval()); - limits.load(network, histoDbClient); - return limits; - } catch (InterruptedException | IOException e) { - throw new RuntimeException(e); + wcaReport.setBaseStateLoadflowResult(new WCALoadflowResult(true,null)); + List baseStateLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + if ( baseStateLimitViolations.size() > 0 ) { + LOGGER.warn("Network {}: constraint violantions found in base state:\n{}", + network.getId(), Security.printLimitsViolations(baseStateLimitViolations, violationsFilter)); + wcaReport.setPreContingencyViolationsWithoutUncertainties(baseStateLimitViolations); + contingencies.forEach(contingency -> filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE,WCAClusterNum.TWO), + WCAClusterOrigin.LF_BASIC_VIOLATION)); + } + Supplier> uncertainties = Suppliers.memoize(() -> { + network.getStateManager().setWorkingState(baseStateId); + try { + if ( !(config.getPreventiveActionsFilter() == WCAPreventiveActionsFilter.DOMAINS) + && !(config.getPreventiveActionsOptimizer() == WCAPreventiveActionsOptimizer.DOMAINS) + && !(parameters.getOfflineWorkflowId() != null) + && !(config.getCurativeActionsOptimizer() == WCACurativeActionsOptimizer.CLUSTERS) ) { + return CompletableFuture.completedFuture(new Uncertainties(Collections.emptyList(), 0)); } - }, computationManager.getExecutor())); - - // get preventive actions for domains task - List preventiveStateIdsForDomains = Collections.synchronizedList(new ArrayList<>()); - List preventiveActionIdsForDomains = Collections.synchronizedList(new ArrayList<>()); - if (baseStateLimitViolations.size() > 0) { - LOGGER.info("Network {}: getting preventive actions for 'domains' task", network.getId()); - List> preventiveActionTasks = Collections.synchronizedList(new ArrayList<>()); - ; - for (LimitViolation baseStateLimitViolation : baseStateLimitViolations) { - List> preventiveActions = contingencyDbFacade.getPreventiveActions(baseStateLimitViolation); - if (preventiveActions.isEmpty()) + LOGGER.info("Network {}: computing uncertainities", network.getId()); + return uncertaintiesAnalyserFactory.create(network, histoDbClient, computationManager).analyse(parameters.getHistoInterval()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + Supplier> histoLimits + = Suppliers.memoize(() -> CompletableFuture.supplyAsync(() -> { + network.getStateManager().setWorkingState(baseStateId); + try { + WCAHistoLimits limits = new WCAHistoLimits(parameters.getHistoInterval()); + if ( config.getPreventiveActionsFilter() == WCAPreventiveActionsFilter.DOMAINS + || config.getPreventiveActionsOptimizer() == WCAPreventiveActionsOptimizer.DOMAINS + || parameters.getOfflineWorkflowId() != null + || config.getCurativeActionsOptimizer() == WCACurativeActionsOptimizer.CLUSTERS ) { + LOGGER.info("Network {}: computing historical limits", network.getId()); + limits.load(network, histoDbClient); + } + return limits; + } catch (InterruptedException | IOException e) { + throw new RuntimeException(e); + } + }, computationManager.getExecutor())); + List violationsToBePrevented = baseStateLimitViolations; + if ( WCAPreventiveActionsFilter.DOMAINS.equals(config.getPreventiveActionsFilter()) ) { + LOGGER.info("Network {}: running 'domains' preventive actions filter", network.getId()); + violationsToBePrevented = createDomainsTaskWithDeps(null, baseStateId, Collections.emptyList(), uncertainties, + histoLimits, mapper, Collections.emptyList(), Collections.emptyList(), + config.activateFiltering()) + .thenCompose(domainsResult -> { + if ( domainsResult.foundBasicViolations() ) { + LOGGER.info("Network {}: 'domains' found basic violations", network.getId()); + String domainsUncertaintiesState = "domainsUncertaintiesState"; + LOGGER.info("Network {}: creating state with 'domains' uncertainties", network.getId()); + network.getStateManager().cloneState(baseStateId, domainsUncertaintiesState); + network.getStateManager().setWorkingState(domainsUncertaintiesState); + WCAUtils.applyInjections(network, domainsUncertaintiesState, domainsResult.getInjections()); + LOGGER.info("Network {}: running loadflow on state with 'domains' uncertainties", network.getId()); + return loadFlow.runAsync(domainsUncertaintiesState, LOAD_FLOW_PARAMETERS) + .thenApply(loadFlowResult -> { + if (!loadFlowResult.isOk()) { + LOGGER.info("Network {}: loadflow on state with 'domains' uncertainties diverged, metrics = {}", network.getId(), loadFlowResult.getMetrics()); + wcaReport.setBaseStateWithUncertaintiesLoadflowResult(new WCALoadflowResult(false,"load flow on state with 'domains' uncertainties diverged: metrics = " + loadFlowResult.getMetrics())); + return CompletableFuture.completedFuture(baseStateLimitViolations); + } else { + List domainsLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + if ( domainsLimitViolations.size() > 0 ) { + LOGGER.warn("Network {}: constraint violantions found in state with 'domains' uncertainties:\n{}", + network.getId(), Security.printLimitsViolations(domainsLimitViolations, violationsFilter)); + wcaReport.setPreContingencyViolationsWithUncertainties(domainsLimitViolations); + contingencies.forEach(contingency -> filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE,WCAClusterNum.TWO), + WCAClusterOrigin.DOMAINS_BASIC_VIOLATION)); + return CompletableFuture.completedFuture(domainsLimitViolations); + } else { + LOGGER.warn("Network {}: no violations found in state with 'domains' uncertainties", network.getId()); + } + return CompletableFuture.completedFuture(baseStateLimitViolations); + } + }).join(); + } + return CompletableFuture.completedFuture(baseStateLimitViolations); + }) + .join(); + network.getStateManager().setWorkingState(baseStateId); + } + LOGGER.info("Network {}: {} violations to be prevented:\n{}", + network.getId(), violationsToBePrevented.size(), Security.printLimitsViolations(violationsToBePrevented, violationsFilter)); + List preventiveStateIdsForDomains = Collections.synchronizedList(new ArrayList<>()); + List preventiveActionIdsForDomains = Collections.synchronizedList(new ArrayList<>()); + Map> possibleActionsToApply = Collections.synchronizedMap(new HashMap>()); + wcaReport.setBaseStateRemainingViolations(violationsToBePrevented); // should I use these violations (they could come from 'domains' worst uncertainties state) or the original basecase violations? + if ( violationsToBePrevented.size() > 0 ) { + if ( config.getPreventiveActionsOptimizer().equals(WCAPreventiveActionsOptimizer.NONE) ) { + contingencies.forEach(contingency -> filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE,WCAClusterNum.TWO, WCAClusterNum.THREE), + null)); + } else { + LOGGER.info("Network {}: getting preventive actions", network.getId()); + violationsToBePrevented.sort((o1, o2) -> (int) Math.ceil(((o2.getValue() - o2.getLimit()) / o2.getValue()) - ((o1.getValue() - o1.getLimit()) / o1.getValue()))); + String previousState = baseStateId; + List solvedViolations = new ArrayList<>(); + List originalViolations = violationsToBePrevented; + violationsLoop: for (LimitViolation violationToBePrevented : violationsToBePrevented) { + List> preventiveActions = contingencyDbFacade.getPreventiveActions(violationToBePrevented); + if ( preventiveActions.isEmpty() ) { continue; + } for (int i = 0; i < preventiveActions.size(); i++) { List preventiveAction = preventiveActions.get(i); String preventiveActionId = preventiveAction.stream().map(Action::getId).collect(Collectors.joining("+")); - if (preventiveStateIdsForDomains.contains(preventiveActionId)) + if ( preventiveStateIdsForDomains.contains(preventiveActionId) ) { continue; - String preventiveStateId = StateManager.INITIAL_STATE_ID + "_" + preventiveActionId; - preventiveActionTasks.add(CompletableFuture.runAsync(() -> { - LOGGER.info("Network {}, Preventive Action {}: starting analysis for {} violation on equipment {}", - network.getId(), - preventiveActionId, - baseStateLimitViolation.getLimitType(), - baseStateLimitViolation.getSubjectId()); - network.getStateManager().cloneState(StateManager.INITIAL_STATE_ID, preventiveStateId); - network.getStateManager().setWorkingState(preventiveStateId); - for (Action subAction : preventiveAction) { - subAction.toTask().modify(network); - } - }, computationManager.getExecutor()) - .thenCompose(ignored -> loadFlow.runAsync(preventiveStateId, LOAD_FLOW_PARAMETERS)) - .thenAccept(loadFlowResult1 -> { - if (loadFlowResult1.isOk()) { - network.getStateManager().setWorkingState(preventiveStateId); - List preventiveStateLimitViolations = CURRENT_FILTER.apply(Security.checkLimits(network)); - Optional notSolvedLimitViolation = preventiveStateLimitViolations - .stream() - .filter(preventiveStateLimitViolation -> preventiveStateLimitViolation.getSubjectId().equals(baseStateLimitViolation.getSubjectId())) - .findAny(); - if (notSolvedLimitViolation.isPresent() && parameters.stopWcaOnViolations()) { - LOGGER.warn("Network {}, Preventive Action {}: post action state still contains {} violation on equiment {}", - network.getId(), - preventiveActionId, - baseStateLimitViolation.getLimitType(), - baseStateLimitViolation.getSubjectId()); - } else { - LOGGER.info("Network {}, Preventive Action {}: adding action to list for 'domains' task", network.getId(), preventiveActionId); - preventiveStateIdsForDomains.add(preventiveStateId); - preventiveActionIdsForDomains.add(preventiveActionId); - } - } else - LOGGER.warn("Network {}, Preventive Action {}: loadflow on post action state diverged", network.getId(), preventiveActionId); - }) - .exceptionally(throwable -> { - if (throwable != null) { - LOGGER.error(throwable.toString(), throwable); + } + String preventiveStateId = previousState + "_" + preventiveActionId; + LOGGER.info("Network {}, preventive action {}: starting analysis for {} violation on equipment {}", + network.getId(), preventiveActionId, violationToBePrevented.getLimitType(), violationToBePrevented.getSubjectId()); + possibleActionsToApply.put(preventiveActionId, preventiveAction); + network.getStateManager().cloneState(previousState, preventiveStateId); + network.getStateManager().setWorkingState(preventiveStateId); + for (Action subAction : preventiveAction) { + subAction.toTask().modify(network, computationManager); + } + LoadFlowResult loadFlowResult1; + try { + loadFlowResult1 = loadFlow.run(LOAD_FLOW_PARAMETERS); + if (loadFlowResult1.isOk()) { + List preventiveStateLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + Optional notSolvedLimitViolation = preventiveStateLimitViolations + .stream() + .filter(preventiveStateLimitViolation -> preventiveStateLimitViolation.getSubjectId().equals(violationToBePrevented.getSubjectId())) + .findAny(); + Optional previouslySolvedLimitViolation = preventiveStateLimitViolations + .stream() + .filter(preventiveStateLimitViolation -> WCAUtils.containsViolation(solvedViolations, preventiveStateLimitViolation)) + .findAny(); + Optional newLimitViolation = preventiveStateLimitViolations + .stream() + .filter(preventiveStateLimitViolation -> !WCAUtils.containsViolation(originalViolations, preventiveStateLimitViolation)) + .findAny(); + if ( notSolvedLimitViolation.isPresent() || previouslySolvedLimitViolation.isPresent() || newLimitViolation.isPresent() ) { + String message = null; + if ( notSolvedLimitViolation.isPresent() ) { + message = "post preventive action state still contains " + violationToBePrevented.getLimitType() + + " violation on equiment " + violationToBePrevented.getSubjectId(); + } else if ( previouslySolvedLimitViolation.isPresent() ) { + message = "post preventive action state contains previously solved violations"; + } else if ( newLimitViolation.isPresent() ) { + message = "post preventive action state contains new violations"; + } + LOGGER.warn("Network {}, preventive action {}: {}:\n{}", + network.getId(), preventiveActionId, message, Security.printLimitsViolations(preventiveStateLimitViolations, violationsFilter)); + if ( !config.filterPreventiveActions() ) { + LOGGER.info("Network {}, preventive action {}: adding anyway preventive action to list (config filterPreventiveActions = false)", + network.getId(), preventiveActionId); + preventiveStateIdsForDomains.add(preventiveStateId); + preventiveActionIdsForDomains.add(preventiveActionId); + previousState = preventiveStateId; + solvedViolations.add(violationToBePrevented); } - return null; - })); + WCAActionApplication actionApplication = new WCAActionApplication(preventiveActionId, + violationToBePrevented, + new WCALoadflowResult(true, null), + false, + false, + message); + actionApplication.setPostActionViolations(preventiveStateLimitViolations); + wcaReport.addPreventiveActionApplication(actionApplication); + } else { + LOGGER.info("Network {}, preventive action {} solves {} violation on equiment {}: adding preventive action to list", + network.getId(), preventiveActionId, violationToBePrevented.getLimitType(), violationToBePrevented.getSubjectId()); + preventiveStateIdsForDomains.add(preventiveStateId); + preventiveActionIdsForDomains.add(preventiveActionId); + previousState = preventiveStateId; + solvedViolations.add(violationToBePrevented); + WCAActionApplication actionApplication = new WCAActionApplication(preventiveActionId, + violationToBePrevented, + new WCALoadflowResult(true, null), + true, + false, + null); + actionApplication.setPostActionViolations(preventiveStateLimitViolations); + wcaReport.addPreventiveActionApplication(actionApplication); + if ( preventiveStateLimitViolations.isEmpty() ) { + LOGGER.info("Network {}, preventive action {} solved all the remaining violations: stopping preventive actions analysis", + network.getId(), preventiveActionId); + break violationsLoop; + } + break; + } + } else { + LOGGER.warn("Network {}, preventive action {}: loadflow on post preventive action state diverged, metrics = {}", + network.getId(), preventiveActionId, loadFlowResult1.getMetrics()); + wcaReport.addPreventiveActionApplication(new WCAActionApplication(preventiveActionId, + violationToBePrevented, + new WCALoadflowResult(false, "loadflow on post preventive action state diverged: metrics = " + loadFlowResult1.getMetrics()), + false, + false, + null)); + } + } catch (Exception e) { + LOGGER.error("Network {}, preventive action {}: loadflow on post preventive action state failed: {}", + network.getId(), preventiveActionId, e.getMessage(), e); + wcaReport.addPreventiveActionApplication(new WCAActionApplication(preventiveActionId, + violationToBePrevented, + new WCALoadflowResult(false, "loadflow on post preventive action state failed: " + e.getMessage()), + false, + false, + null)); + } } - } - try { - CompletableFuture.allOf(preventiveActionTasks.toArray(new CompletableFuture[preventiveActionTasks.size()])).get(); - } catch (Exception e) { - LOGGER.error("Network {}: error getting preventive actions for 'domains' task: {}", network.getId(), e.getMessage(), e); + LOGGER.info("Network {}: found {} preventive actions", network.getId(), preventiveActionIdsForDomains.size()); + Collection preventiveActionsToApply = CompletableFuture.completedFuture(new ArrayList()) + .thenCompose( ignored -> { + if ( config.getPreventiveActionsOptimizer().equals(WCAPreventiveActionsOptimizer.DOMAINS) + && !preventiveActionIdsForDomains.isEmpty() ) { + LOGGER.info("Network {}: running 'domains' preventive actions optimizer", network.getId()); + return createDomainsTaskWithDeps(null, baseStateId, Collections.emptyList(), uncertainties, histoLimits, mapper, + preventiveStateIdsForDomains, preventiveActionIdsForDomains, config.activateFiltering()) + .thenCompose(domainsResult -> { + if ( domainsResult.getPreventiveActionIndex() > 0 ) { + LOGGER.info("Network {}: 'domains' found {} preventive actions solved the violations", + network.getId(), preventiveActionIdsForDomains.subList(0, domainsResult.getPreventiveActionIndex())); + contingencies.forEach(contingency -> filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE,WCAClusterNum.TWO), + WCAClusterOrigin.DOMAINS_SPECIFIC_PREVENTIVE_ACTION_FOUND)); + } else { + LOGGER.info("Network {}: 'domains' found no preventive actions solving the violations", network.getId()); + contingencies.forEach(contingency -> filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE,WCAClusterNum.TWO, WCAClusterNum.THREE), + WCAClusterOrigin.DOMAINS_NO_PREVENTIVE_ACTION_FOUND)); + } + return CompletableFuture.completedFuture(new ArrayList(preventiveActionIdsForDomains.subList(0, domainsResult.getPreventiveActionIndex()))); + }); + } + return CompletableFuture.completedFuture(new ArrayList()); + }) + .join(); + if ( preventiveActionsToApply.size() > 0 && config.applyPreventiveActions() ) { + LOGGER.info("Network {}: applying preventive actions", network.getId()); + network.getStateManager().setWorkingState(baseStateId); + CompletableFuture.runAsync(() -> { + preventiveActionsToApply.forEach(actionId -> { + if ( possibleActionsToApply.containsKey(actionId) ) { + possibleActionsToApply.get(actionId).forEach(action -> { + LOGGER.info("Network {}: applying preventive action {}, elementary action {}", network.getId(), actionId, action.getId()); + action.toTask().modify(network, computationManager); + }); + wcaReport.setPreventiveActionAsApplied(actionId); + } else { + LOGGER.warn("Network {}: action {} not found in preventive actions", network.getId(), actionId); + } + }); + }, computationManager.getExecutor()) + .thenCompose(ignored -> loadFlow.runAsync(baseStateId, LOAD_FLOW_PARAMETERS)) + .thenAccept(ignored -> { + // check that the violations have disappeared? this check has already been done previously + // update basecase remaining violations + wcaReport.setBaseStateRemainingViolations(violationsFilter.apply(Security.checkLimits(network))); + }) + .exceptionally(throwable -> { + if (throwable != null) { + LOGGER.error(throwable.toString(), throwable); + } + return null; + }) + .join(); + } + if ( contingencies.stream().allMatch( + contingency -> ( filteredClusters.getClusters(contingency.getId()).size() == 1 + && WCAClusterNum.FOUR.equals(filteredClusters.getCluster(contingency.getId())) ) + ) || config.loosenConstraints() ) { + wcaReport.setPostPreventiveActionsViolationsWithUncertainties(wcaReport.getBaseStateRemainingViolations()); + if ( !wcaReport.getBaseStateRemainingViolations().isEmpty() ) { + LOGGER.warn("Network {}: loosening the basecase constraints for remaining violations:\n{}", + network.getId(), Security.printLimitsViolations(new ArrayList<>(wcaReport.getBaseStateRemainingViolations()), violationsFilter)); + ConstraintsModifierConfig constraintsModifierConfig = new ConstraintsModifierConfig( + config.getCountryConstraintFilter(), + config.ignoreVoltageConstraints() ? EnumSet.of(LimitViolationType.CURRENT) : EnumSet.allOf(LimitViolationType.class) + ); + ConstraintsModifier constraintsModifier = new ConstraintsModifier(network, constraintsModifierConfig); + constraintsModifier.looseConstraints(baseStateId, + new ArrayList<>(wcaReport.getBaseStateRemainingViolations()), + config.getMargin()); + } } - LOGGER.info("Network {}: found {} preventive actions for 'domains' task", network.getId(), preventiveActionIdsForDomains.size()); - } - // check offline rules - for (Contingency contingency : contingencies) { - clusters.add(CompletableFuture - .completedFuture(null) - .thenComposeAsync(ignored -> { - network.getStateManager().setWorkingState(baseStateId); - - List causes = new ArrayList<>(); - List securityRuleExpressions = new ArrayList<>(); - if (parameters.getOfflineWorkflowId() != null) { - Supplier> baseStateAttributeValues = Suppliers.memoize((Supplier>) () -> IIDM2DB.extractCimValues(network, new IIDM2DB.Config(null, false)).getSingleValueMap()); - - for (RuleAttributeSet attributeSet : RuleAttributeSet.values()) { - for (SecurityIndexType securityIndexType : getSecurityIndexTypes(parameters)) { - List securityRules = rulesDbClient.getRules(parameters.getOfflineWorkflowId(), attributeSet, contingency.getId(), securityIndexType); - if (securityRules.isEmpty()) { - causes.add("Missing rule " + new RuleId(attributeSet, new SecurityIndexId(contingency.getId(), securityIndexType))); - } else { - for (SecurityRule securityRule : securityRules) { - SecurityRuleExpression expression = securityRule.toExpression(parameters.getPurityThreshold()); + } + } + for (Contingency contingency : contingencies) { + LOGGER.info("Network {}, contingency {}: starting analysis", network.getId(), contingency.getId()); + clusters.add(CompletableFuture + .completedFuture(null) + .thenComposeAsync(ignored -> { + network.getStateManager().setWorkingState(baseStateId); + boolean rulesViolated = false; + List securityRuleExpressions = new ArrayList<>(); + if (parameters.getOfflineWorkflowId() != null) { + LOGGER.info("Network {}, contingency {}: starting security rules analysis", network.getId(), contingency.getId()); + Supplier> baseStateAttributeValues = Suppliers + .memoize((Supplier>) () -> IIDM2DB + .extractCimValues(network, new IIDM2DB.Config(null,false)) + .getSingleValueMap()); + for (RuleAttributeSet attributeSet : RuleAttributeSet.values()) { + for (SecurityIndexType securityIndexType : getSecurityIndexTypes(parameters)) { + List securityRules = rulesDbClient.getRules(parameters.getOfflineWorkflowId(), + attributeSet, + contingency.getId(), + securityIndexType); + if (securityRules.isEmpty()) { + rulesViolated = true; + String cause = "Missing rule " + new RuleId(attributeSet, new SecurityIndexId(contingency.getId(), securityIndexType)); + LOGGER.warn("Network {}, contingency {}: {}", network.getId(), contingency.getId(), cause); + wcaReport.addSecurityRulesApplication(new WCASecurityRuleApplication(contingency.getId(), + null, + true, + WCARuleViolationType.MISSING_RULE, + cause)); + } else { + for (SecurityRule securityRule : securityRules) { + LOGGER.info("Network {}, contingency {}: checking rule {}", + network.getId(), contingency.getId(), securityRule.getId()); + String cause = "Rule " + securityRule.getId() + " verified"; + boolean isRuleViolated = false; + WCARuleViolationType violationType = WCARuleViolationType.NO_VIOLATION; + SecurityRuleExpression expression = securityRule.toExpression(parameters.getPurityThreshold()); + SecurityRuleCheckReport checkReport = expression.check(baseStateAttributeValues.get()); + if (checkReport.getMissingAttributes().size() > 0) { + rulesViolated = true; + isRuleViolated = true; + violationType = WCARuleViolationType.MISSING_ATTRIBUTE; + cause = "Missing attributes for rule " + securityRule.getId() + + ": " + checkReport.getMissingAttributes().stream().map(Object::toString).collect(Collectors.joining(",")); + LOGGER.warn("Network {}, contingency {}: {}", network.getId(), contingency.getId(), cause); + } else if (!checkReport.isSafe()) { + rulesViolated = true; + isRuleViolated = true; + violationType = WCARuleViolationType.RULE_NOT_SAFE; + cause = "Rule " + securityRule.getId() + " not verified"; + LOGGER.warn("Network {}, contingency {}: {}", network.getId(), contingency.getId(), cause); + } else { + LOGGER.info("Network {}, contingency {}: adding rule {} to the list for analysis", + network.getId(), contingency.getId(), securityRule.getId()); securityRuleExpressions.add(expression); - SecurityRuleCheckReport checkReport = expression.check(baseStateAttributeValues.get()); - if (checkReport.getMissingAttributes().size() > 0) { - causes.add("Missing attributes for rule " + securityRule.getId() - + ": " + checkReport.getMissingAttributes().stream().map(Object::toString).collect(Collectors.joining(","))); - } else if (!checkReport.isSafe()) { - causes.add("Rule " + securityRule.getId() + " not verified"); - } } + wcaReport.addSecurityRulesApplication(new WCASecurityRuleApplication(contingency.getId(), + securityRule, + isRuleViolated, + violationType, + cause)); } } } } - if (causes.size() > 0 && parameters.stopWcaOnViolations()) { - return CompletableFuture.completedFuture(new WCAClusterImpl(contingency, - WCAClusterNum.FOUR, - WCAClusterOrigin.HADES_BASE_OFFLINE_RULE, - causes)); - } else { - return createDomainTaskWithDeps(contingency, baseStateId, securityRuleExpressions, uncertainties, histoLimits, mapper, - preventiveStateIdsForDomains, preventiveActionIdsForDomains, parameters.stopWcaOnViolations()) - .thenCompose(cluster -> { - if (cluster != null && parameters.stopWcaOnViolations()) { - return CompletableFuture.completedFuture(cluster); - } - return createClusterWorkflowTask(contingency, baseStateId, contingencyDbFacade, securityRuleExpressions, - uncertainties, histoLimits, mapper, loadFlow, parameters.stopWcaOnViolations()); - }); - } + } + if ( rulesViolated ) { + filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE), + WCAClusterOrigin.LF_RULE_VIOLATION); + } + return CompletableFuture + .completedFuture(rulesViolated) + .thenCompose( rulesViolationsFound -> { + if ( securityRuleExpressions.isEmpty() || (rulesViolationsFound && config.activateFiltering()) ) { + return CompletableFuture.completedFuture(new WCADomainsResult()); + } + LOGGER.info("Network {}: running 'domains' with security rules for contingency {}", network.getId(), contingency.getId()); + return createDomainsTaskWithDeps(contingency, baseStateId, securityRuleExpressions, uncertainties, histoLimits, + mapper, preventiveStateIdsForDomains, preventiveActionIdsForDomains, + config.activateFiltering()); + }) + .thenCompose( domainsResult -> { + if ( domainsResult.areRulesViolated() ) { + filteredClusters.removeClusters(contingency.getId(), + EnumSet.of(WCAClusterNum.ONE), + WCAClusterOrigin.DOMAINS_RULE_VIOLATION); + } + if ( domainsResult.areRulesViolated() && config.activateFiltering() ) { + return CompletableFuture.completedFuture(WCAClusterNum.UNDEFINED); + } + return createClustersWorkflowTask(contingency, baseStateId, contingencyDbFacade, securityRuleExpressions, + uncertainties, histoLimits, mapper, loadFlow, config.activateFiltering(), + config.filterCurativeActions(), filteredClusters); + }) + .thenCompose( clusterNum -> { + LOGGER.info("Network {}, contingency {}: assigned cluster number {}", + network.getId(), contingency.getId(), clusterNum); + if ( !clusterNum.equals(WCAClusterNum.UNDEFINED) + && !filteredClusters.getFlags(contingency.getId()).contains(WCAClusterOrigin.LF_POST_CONTINGENCY_DIVERGENCE) ) { + if ( filteredClusters.hasCluster(contingency.getId(), clusterNum) ) { + filteredClusters.removeAllButCluster(contingency.getId(), + clusterNum, + WCAClusterOrigin.CLUSTERS_ANALYSIS); + } else { + filteredClusters.addClusters(contingency.getId(), + EnumSet.of(clusterNum), + WCAClusterOrigin.CLUSTERS_ANALYSIS); + } + } + LOGGER.info("Network {}, contingency {}: final cluster number {}", + network.getId(), contingency.getId(), filteredClusters.getCluster(contingency.getId())); + LOGGER.info("Network {}, contingency {}: final flags {}", + network.getId(), contingency.getId(), filteredClusters.getFlags(contingency.getId())); + return CompletableFuture.completedFuture(new WCAClusterImpl(contingency, + filteredClusters.getCluster(contingency.getId()), + filteredClusters.getFlags(contingency.getId()), + Collections.emptyList())); + }); }, computationManager.getExecutor())); - } } } @@ -803,7 +1122,7 @@ private CompletableFuture>> createWcaTask(Str @Override public CompletableFuture runAsync(String baseStateId, WCAParameters parameters) throws Exception { LOGGER.info(parameters.toString()); - LOGGER.info("Starting WCA..."); + LOGGER.info("Network {}: starting WCA...", network.getId()); return createWcaTask(baseStateId, parameters) .thenApply(clusters -> () -> clusters); } @@ -819,4 +1138,9 @@ public WCAResult run(WCAParameters parameters) throws Exception { return () -> clusters; } + @Override + public WCAReport getReport() { + return wcaReport; + } + } diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCAPreventiveActionsFilter.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCAPreventiveActionsFilter.java new file mode 100644 index 00000000..37633cdd --- /dev/null +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCAPreventiveActionsFilter.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + + +/** + * + * @author Massimo Ferraro + */ +public enum WCAPreventiveActionsFilter { + LF, + DOMAINS +} diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCAPreventiveActionsOptimizer.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCAPreventiveActionsOptimizer.java new file mode 100644 index 00000000..50b7c7ac --- /dev/null +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCAPreventiveActionsOptimizer.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +/** + * + * @author Massimo Ferraro + */ +public enum WCAPreventiveActionsOptimizer { + NONE, + LF_HEURISTIC, + DOMAINS +} diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCASecurityRulesWriter.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCASecurityRulesWriter.java index 6438591c..a158bbc2 100644 --- a/wca-integration/src/main/java/eu/itesla_project/wca/WCASecurityRulesWriter.java +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCASecurityRulesWriter.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) - * Copyright (c) 2016, RTE (http://www.rte-france.com) + * Copyright (c) 2016-2017, 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/. @@ -49,15 +49,16 @@ public class WCASecurityRulesWriter implements AmplConstants, WCAConstants { private final boolean debug; - private final boolean stopWcaOnViolations; + private final boolean activateFiltering; - public WCASecurityRulesWriter(Network network, List rules, DataSource dataSource, StringToIntMapper mapper, boolean debug, boolean stopWcaOnViolations) { + public WCASecurityRulesWriter(Network network, List rules, DataSource dataSource, + StringToIntMapper mapper, boolean debug, boolean activateFiltering) { this.dataSource = Objects.requireNonNull(dataSource); this.network = Objects.requireNonNull(network); this.rules = Objects.requireNonNull(rules); this.mapper = Objects.requireNonNull(mapper); this.debug = debug; - this.stopWcaOnViolations = stopWcaOnViolations; + this.activateFiltering = activateFiltering; } private static class WCAEntity { @@ -177,7 +178,7 @@ class Context { final int attributeSetNum = attributeSet.ordinal(); if (rule.getStatus() == SecurityRuleStatus.ALWAYS_UNSECURE) { - if ( stopWcaOnViolations ) + if ( activateFiltering ) throw new RuntimeException("Always unsecure rule " + ruleId); else continue; diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCAUtils.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCAUtils.java index 357251de..fff233c1 100644 --- a/wca-integration/src/main/java/eu/itesla_project/wca/WCAUtils.java +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCAUtils.java @@ -6,20 +6,51 @@ */ package eu.itesla_project.wca; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import eu.itesla_project.commons.io.table.Column; +import eu.itesla_project.commons.io.table.TableFormatter; +import eu.itesla_project.commons.util.StringToIntMapper; +import eu.itesla_project.contingency.Contingency; import eu.itesla_project.iidm.datasource.DataSource; import eu.itesla_project.iidm.datasource.GzFileDataSource; import eu.itesla_project.iidm.export.Exporters; +import eu.itesla_project.iidm.export.ampl.AmplConstants; +import eu.itesla_project.iidm.export.ampl.AmplSubset; +import eu.itesla_project.iidm.export.ampl.util.AmplDatTableFormatter; +import eu.itesla_project.iidm.network.Generator; +import eu.itesla_project.iidm.network.Load; import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.modules.wca.WCAClusterNum; +import eu.itesla_project.security.LimitViolation; /** * * @author Massimo Ferraro */ public final class WCAUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(WCAUtils.class); private WCAUtils() { } @@ -33,5 +64,202 @@ public static void exportState(Network network, Path folder, int faultNum, int a DataSource dataSource = new GzFileDataSource(folder, network.getId() + "_" + faultNum + "_" + actionNum); Exporters.export("XIIDM", network, parameters, dataSource); } + + private static Matcher parseOutFile(String prefix, Pattern pattern, Path workingDir) throws IOException { + Path out = workingDir.resolve(prefix + "_0.out"); + Path outGz = workingDir.resolve(prefix + "_0.out.gz"); + if (Files.exists(out) || Files.exists(outGz)) { + try (BufferedReader reader = (Files.exists(out)) ? Files.newBufferedReader(out, StandardCharsets.UTF_8) + : new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(outGz.toFile()))))) { + String line; + while ((line = reader.readLine()) != null) { + Matcher matcher = pattern.matcher(line); + if (matcher.matches()) { + return matcher; + } + } + } + } else { + LOGGER.error("WCA output file {} or {} not found !!", out.toFile().getAbsolutePath(), outGz.toFile().getAbsolutePath()); + } + return null; + } + + private static Map parseUncertaintiesFile(String uncertaintiesFile, Path workingDir) throws IOException { + Map injections = new HashMap(); + Path out = workingDir.resolve(uncertaintiesFile); + Path outGz = workingDir.resolve(uncertaintiesFile + ".gz"); + if (Files.exists(out) || Files.exists(outGz)) { + try (BufferedReader reader = (Files.exists(out)) ? Files.newBufferedReader(out, StandardCharsets.UTF_8) + : new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(outGz.toFile()))))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.trim().isEmpty()) { + continue; + } + String[] tokens = line.split(";"); + injections.put(tokens[0].replaceAll("^\"|\"$", ""), Float.parseFloat(tokens[1])); + } + } + } else { + LOGGER.error("WCA injections file {} or {} not found !!", out.toFile().getAbsolutePath(), outGz.toFile().getAbsolutePath()); + } + return injections; + } + + private static boolean flowsWithViolations(String flowsFile, Path workingDir) throws IOException { + Path out = workingDir.resolve(flowsFile); + Path outGz = workingDir.resolve(flowsFile + ".gz"); + if (Files.exists(out) || Files.exists(outGz)) { + try (BufferedReader reader = (Files.exists(out)) ? Files.newBufferedReader(out, StandardCharsets.UTF_8) + : new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(outGz.toFile()))))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.trim().isEmpty()) { + continue; + } + String[] tokens = line.split(";"); + if ( Math.abs(Float.parseFloat(tokens[1])) > Math.abs(Float.parseFloat(tokens[2])) ) { + LOGGER.info("Folder {}: found flow violation on branch {}", workingDir, tokens[0]); + return true; + } + } + } + } else { + LOGGER.error("WCA flows file {} or {} not found !!", out.toFile().getAbsolutePath(), outGz.toFile().getAbsolutePath()); + } + return false; + } + + private static final Pattern DOMAINS_RESULTS_PATTERN = Pattern.compile(" WCA Result : basic_violation (\\d*) rule_violation (\\d*) preventive_action_index (\\d*)"); + + public static WCADomainsResult readDomainsResult(String domainsPrefix, Path workingDir, String uncertaintiesFile) throws IOException { + Objects.requireNonNull(domainsPrefix); + Objects.requireNonNull(workingDir); + Objects.requireNonNull(uncertaintiesFile); + boolean foundBasicViolations = false; + boolean rulesViolated = false; + int preventiveActionIndex = 0; + Matcher matcher = parseOutFile(domainsPrefix, DOMAINS_RESULTS_PATTERN, workingDir); + if (matcher != null) { + int basicViolation = Integer.parseInt(matcher.group(1)); + int ruleViolation = Integer.parseInt(matcher.group(2)); + preventiveActionIndex = Integer.parseInt(matcher.group(3)); + if (basicViolation == 1) { + foundBasicViolations = true; + } + if (ruleViolation == 1) { + rulesViolated = true; + } + } + return new WCADomainsResult(foundBasicViolations, rulesViolated, preventiveActionIndex, parseUncertaintiesFile(uncertaintiesFile, workingDir)); + } + + private static final Pattern CLUSTER_INDEX_PATTERN = Pattern.compile(" WCA Result : contingency_index (\\d*) contingency_cluster_index (\\d*) curative_action_index (\\d*)"); + + public static WCAClustersResult readClustersResult(String clustersPrefix, Path workingDir, String flowsFile, String uncertaintiesFile) throws IOException { + Objects.requireNonNull(clustersPrefix); + Objects.requireNonNull(workingDir); + Objects.requireNonNull(uncertaintiesFile); + WCAClusterNum clusterNum = WCAClusterNum.UNDEFINED; + int curativeActionIndex = 0; + Matcher matcher = parseOutFile(clustersPrefix, CLUSTER_INDEX_PATTERN, workingDir); + if (matcher != null) { + clusterNum = WCAClusterNum.fromInt(Integer.parseInt(matcher.group(2))); + curativeActionIndex = Integer.parseInt(matcher.group(3)); + } + return new WCAClustersResult(clusterNum, flowsWithViolations(flowsFile, workingDir), curativeActionIndex, + parseUncertaintiesFile(uncertaintiesFile, workingDir)); + } + + public static void writeContingencies(Collection contingencies, DataSource dataSource, StringToIntMapper mapper) { + Objects.requireNonNull(contingencies); + Objects.requireNonNull(dataSource); + Objects.requireNonNull(mapper); + try (TableFormatter formatter = new AmplDatTableFormatter( + new OutputStreamWriter(dataSource.newOutputStream(WCAConstants.FAULTS_FILE_SUFFIX, WCAConstants.TXT_EXT, false), StandardCharsets.UTF_8), + "Contingencies", + AmplConstants.INVALID_FLOAT_VALUE, + true, + AmplConstants.LOCALE, + new Column("num"), + new Column("id"))) { + for (Contingency contingency : contingencies) { + int contingencyNum = mapper.getInt(AmplSubset.FAULT, contingency.getId()); + formatter.writeCell(contingencyNum) + .writeCell(contingency.getId()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static void writeActions(Collection actionIds, DataSource dataSource, StringToIntMapper mapper, + String title, AmplSubset amplSubset) { + Objects.requireNonNull(actionIds); + Objects.requireNonNull(dataSource); + Objects.requireNonNull(mapper); + Objects.requireNonNull(title); + Objects.requireNonNull(amplSubset); + try (TableFormatter formatter = new AmplDatTableFormatter( + new OutputStreamWriter(dataSource.newOutputStream(WCAConstants.ACTIONS_FILE_SUFFIX, WCAConstants.TXT_EXT, false), StandardCharsets.UTF_8), + title, + AmplConstants.INVALID_FLOAT_VALUE, + true, + AmplConstants.LOCALE, + new Column("num"), + new Column("id"))) { + for (String actionId : actionIds) { + int actionNum = mapper.getInt(amplSubset, actionId); + formatter.writeCell(actionNum) + .writeCell(actionId); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static void applyInjections(Network network, String stateId, Map injections) { + Objects.requireNonNull(network); + Objects.requireNonNull(stateId); + Objects.requireNonNull(injections); + String originalStateId = network.getStateManager().getWorkingStateId(); + network.getStateManager().setWorkingState(stateId); + injections.keySet().forEach(injection -> + { + Load load = network.getLoad(injection); + if ( load != null ) { + float oldP = load.getTerminal().getP(); + LOGGER.debug("Network {}, state {}: incrementing P of load {} from {} to {}", + network.getId(), network.getStateManager().getWorkingStateId(), injection, oldP, oldP + injections.get(injection)); + load.getTerminal().setP(oldP + injections.get(injection)); + load.setP0(oldP + injections.get(injection)); + } else { + Generator generator = network.getGenerator(injection); + if ( generator != null ) { + float oldP = generator.getTerminal().getP(); + LOGGER.debug("Network {}, state {}: incrementing P of generator {} from {} to {}", + network.getId(), network.getStateManager().getWorkingStateId(), injection, oldP, oldP + injections.get(injection)); + generator.getTerminal().setP(oldP + injections.get(injection)); + generator.setTargetP(-oldP - injections.get(injection)); + } else { + LOGGER.error("No load or generator with id {} in network {}: cannot apply the injection", injection, network.getId()); + } + + } + }); + network.getStateManager().setWorkingState(originalStateId); + } + + public static boolean containsViolation(List violations, LimitViolation violation) { + Objects.requireNonNull(violations); + Objects.requireNonNull(violation); + Optional foundLimitViolation = violations + .stream() + .filter(limitViolation -> limitViolation.getSubjectId().equals(violation.getSubjectId()) + && limitViolation.getLimitType().equals(violation.getLimitType())) + .findAny(); + return foundLimitViolation.isPresent(); + } } diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/report/WCAReportImpl.java b/wca-integration/src/main/java/eu/itesla_project/wca/report/WCAReportImpl.java new file mode 100644 index 00000000..8e6ba785 --- /dev/null +++ b/wca-integration/src/main/java/eu/itesla_project/wca/report/WCAReportImpl.java @@ -0,0 +1,458 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca.report; + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import eu.itesla_project.commons.io.table.Column; +import eu.itesla_project.commons.io.table.CsvTableFormatterFactory; +import eu.itesla_project.commons.io.table.TableFormatter; +import eu.itesla_project.commons.io.table.TableFormatterConfig; +import eu.itesla_project.modules.wca.report.WCAActionApplication; +import eu.itesla_project.modules.wca.report.WCALoadflowResult; +import eu.itesla_project.modules.wca.report.WCAPostContingencyStatus; +import eu.itesla_project.modules.wca.report.WCAReport; +import eu.itesla_project.modules.wca.report.WCASecurityRuleApplication; +import eu.itesla_project.security.LimitViolation; + +/** + * + * @author Massimo Ferraro + */ +public class WCAReportImpl implements WCAReport { + + private final static Logger LOGGER = LoggerFactory.getLogger(WCAReportImpl.class); + public static String PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE = "pre-contigency-violations-without-uncertainties-report.csv"; + public static String PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE = "pre-contigency-violations-without-uncertainties"; + public static String PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE = "pre-contigency-violations-with-uncertainties-report.csv"; + public static String PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE = "pre-contigency-violations-with-uncertainties"; + public static String POST_PREVENTIVE_ACTIONS_FILE = "post-preventive-actions-report.csv"; + public static String POST_PREVENTIVE_ACTIONS_TITLE = "post-preventive-actions"; + public static String POST_PREVENTIVE_ACTIONS_VIOLATIONS_WITH_UNCERTAINTIES_FILE = "post-preventive-actions-violations-with-uncertainties-report.csv"; + public static String POST_PREVENTIVE_ACTIONS_VIOLATIONS_WITH_UNCERTAINTIES_TITLE = "post-preventive-actions-violations-with-uncertainties"; + public static String SECURITY_RULES_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE = "security-rules-violations-without-uncertainties-report.csv"; + public static String SECURITY_RULES_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE = "security-rules-violations-without-uncertainties"; + public static String POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE = "post-contigency-violations-without-uncertainties-report.csv"; + public static String POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE = "post-contigency-violations-without-uncertainties"; + public static String POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE = "post-contigency-violations-with-uncertainties-report.csv"; + public static String POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE = "post-contigency-violations-with-uncertainties"; + public static String POST_CURATIVE_ACTIONS_FILE = "post-curative-actions-report.csv"; + public static String POST_CURATIVE_ACTIONS_TITLE = "post-curative-actions"; + private static TableFormatterConfig TABLE_FORMATTER_CONFIG = TableFormatterConfig.load(); + private static String LOADFLOW_STEP = "Loadflow"; + + private final String basecase; + private WCALoadflowResult baseStateLoadflowResult; + private List preContingencyViolationsWithoutUncertainties = Collections.synchronizedList(new ArrayList<>()); + private WCALoadflowResult baseStateWithUncertaintiesLoadflowResult; + private List preContingencyViolationsWithUncertainties = Collections.synchronizedList(new ArrayList<>()); + private Map preventiveActionsApplication = Collections.synchronizedMap(new HashMap()); + private List postPreventiveActionsViolationsWithUncertainties = Collections.synchronizedList(new ArrayList<>()); + private List baseStateRemainingViolations = Collections.synchronizedList(new ArrayList<>()); + private List securityRulesApplication = Collections.synchronizedList(new ArrayList<>()); + private List postContingenciesStatus = Collections.synchronizedList(new ArrayList<>()); + + public WCAReportImpl(String basecase) { + this.basecase = Objects.requireNonNull(basecase); + } + + @Override + public String getBasecase() { + return basecase; + } + + @Override + public WCALoadflowResult getBaseStateLoadflowResult() { + return baseStateLoadflowResult; + } + + @Override + public List getPreContingencyViolationsWithoutUncertainties() { + return preContingencyViolationsWithoutUncertainties; + } + + @Override + public WCALoadflowResult getBaseStateWithUncertaintiesLoadflowResult() { + return baseStateWithUncertaintiesLoadflowResult; + } + + @Override + public List getPreContingencyViolationsWithUncertainties() { + return preContingencyViolationsWithUncertainties; + } + + @Override + public List getPreventiveActionsApplication() { + return new ArrayList<>(preventiveActionsApplication.values()); + } + + @Override + public List getPostPreventiveActionsViolationsWithUncertainties() { + return postPreventiveActionsViolationsWithUncertainties; + } + + @Override + public List getBaseStateRemainingViolations() { + return baseStateRemainingViolations; + } + + @Override + public List getSecurityRulesApplication() { + return securityRulesApplication; + } + + @Override + public List getPostContingenciesStatus() { + return postContingenciesStatus; + } + + @Override + public boolean exportCsv(Path folder) { + Objects.requireNonNull(folder); + try { + if ( !Files.exists(folder) ) { + Files.createDirectories(folder); + } else if ( !Files.isDirectory(folder) ) + throw new RuntimeException(folder + " is a file, not a folder"); + exportPreContingencyViolationsWithoutUncertainties(folder); + exportPreContingencyViolationsWithUncertainties(folder); + exportPreventiveActionsApplication(folder); + exportPostPreventiveActionsViolationsWithUncertainties(folder); + exportSecurityRulesApplication(folder); + exportPostContingencyViolations(folder); + exportCurativeActionsApplication(folder); + return true; + } catch (Throwable e) { + LOGGER.error("Error exporting WCA report of basecase {}: {}", basecase, e.getMessage(), e); + return false; + } + } + + public void setBaseStateLoadflowResult(WCALoadflowResult wcaLoadflowResult) { + this.baseStateLoadflowResult = wcaLoadflowResult; + } + + public void setPreContingencyViolationsWithoutUncertainties(List preContingencyViolations) { + this.preContingencyViolationsWithoutUncertainties = Objects.requireNonNull(preContingencyViolations); + } + + public void setBaseStateWithUncertaintiesLoadflowResult(WCALoadflowResult wcaLoadflowResult) { + this.baseStateWithUncertaintiesLoadflowResult = wcaLoadflowResult; + } + + public void setPreContingencyViolationsWithUncertainties(List preContingencyViolations) { + this.preContingencyViolationsWithUncertainties = Objects.requireNonNull(preContingencyViolations); + } + + public void addPreventiveActionApplication(WCAActionApplication actionApplication) { + Objects.requireNonNull(actionApplication); + preventiveActionsApplication.put(actionApplication.getActionId(), actionApplication); + } + + public void setPreventiveActionAsApplied(String actionId) { + Objects.requireNonNull(actionId); + if ( preventiveActionsApplication.containsKey(actionId)) + preventiveActionsApplication.get(actionId).setActionApplied(true);; + } + + public void setPostPreventiveActionsViolationsWithUncertainties(List postPreventiveActionsViolations) { + this.postPreventiveActionsViolationsWithUncertainties = Objects.requireNonNull(postPreventiveActionsViolations); + } + + public void setBaseStateRemainingViolations(List baseStateRemainingViolations) { + this.baseStateRemainingViolations = Objects.requireNonNull(baseStateRemainingViolations); + } + + public void addSecurityRulesApplication(WCASecurityRuleApplication securityRuleApplication) { + Objects.requireNonNull(securityRuleApplication); + securityRulesApplication.add(securityRuleApplication) ; + } + + public void addPostContingencyStatus(WCAPostContingencyStatus postContingencyStatus) { + Objects.requireNonNull(postContingencyStatus); + postContingenciesStatus.add(postContingencyStatus); + } + + private void writeViolations(TableFormatter formatter, String contingencyId, WCALoadflowResult loadflowResult, + List violations) { + if (loadflowResult != null && !loadflowResult.loadflowConverged()) { + try { + formatter.writeCell(basecase); + if ( contingencyId != null ) { + formatter.writeCell(contingencyId); + } + formatter.writeCell(LOADFLOW_STEP) + .writeCell(loadflowResult.getComment()) + .writeEmptyCell() + .writeEmptyCell() + .writeEmptyCell() + .writeEmptyCell() + .writeEmptyCell() + .writeEmptyCell(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else if ( !violations.isEmpty() ) { + violations.forEach(violation -> { + try { + formatter.writeCell(basecase); + if ( contingencyId != null ) { + formatter.writeCell(contingencyId); + } + formatter.writeEmptyCell() + .writeEmptyCell() + .writeCell(violation.getLimitType().name()) + .writeCell(violation.getSubjectId()) + .writeCell(violation.getValue()) + .writeCell(violation.getLimit()) + .writeCell(violation.getCountry().name()) + .writeCell(violation.getBaseVoltage()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + } + + private void exportViolations(Path folder, String file, String title, WCALoadflowResult loadflowResult, + List violations) throws IOException { + Path violationsPath = folder.resolve(file); + Column[] COLUMNS = { + new Column("Basecase"), + new Column("FailureStep"), + new Column("FailureDescription"), + new Column("ViolationType"), + new Column("Equipment"), + new Column("Value"), + new Column("Limit"), + new Column("Country"), + new Column("BaseVoltage") + }; + CsvTableFormatterFactory factory = new CsvTableFormatterFactory(); + try (Writer writer = Files.newBufferedWriter(violationsPath, StandardCharsets.UTF_8); + TableFormatter formatter = factory.create(writer, title, TABLE_FORMATTER_CONFIG, COLUMNS)) { + writeViolations(formatter, null, loadflowResult, violations); + } + } + private void exportPreContingencyViolationsWithoutUncertainties(Path folder) throws IOException { + LOGGER.info("Exporting pre-contingency violations without uncertainties report of basecase {} to file {}", + basecase, folder + File.separator + PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); + exportViolations(folder, + PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE, + PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, + baseStateLoadflowResult, + preContingencyViolationsWithoutUncertainties); + } + + private void exportPreContingencyViolationsWithUncertainties(Path folder) throws IOException { + LOGGER.info("Exporting pre-contingency violations with uncertainties report of basecase {} to file {}", + basecase, folder + File.separator + PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE); + exportViolations(folder, + PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE, + PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, + baseStateWithUncertaintiesLoadflowResult, + preContingencyViolationsWithUncertainties); + } + + private void writeActionsApplications(TableFormatter formatter, String contingencyId, + List actionsApplication) { + if ( !actionsApplication.isEmpty() ) { + actionsApplication.forEach( actionApplication -> { + try { + formatter.writeCell(basecase); + if ( contingencyId != null ) { + formatter.writeCell(contingencyId); + } + formatter.writeCell(actionApplication.getActionId()); + if ( actionApplication.getViolation() != null ) { + formatter.writeCell(actionApplication.getViolation().getSubjectId()) + .writeCell(actionApplication.getViolation().getLimitType().name()); + } else { + formatter.writeEmptyCell() + .writeEmptyCell(); + } + if ( !actionApplication.getLoadflowResult().loadflowConverged() ) { + formatter.writeCell(LOADFLOW_STEP) + .writeCell(actionApplication.getLoadflowResult().getComment()); + } else { + formatter.writeEmptyCell() + .writeEmptyCell(); + } + formatter.writeCell(actionApplication.areViolationsRemoved()) + .writeCell(actionApplication.isActionApplied()); + if ( actionApplication.getComment() != null ) { + formatter.writeCell(actionApplication.getComment()); + } else { + formatter.writeEmptyCell(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + } + + private void exportPreventiveActionsApplication(Path folder) throws IOException { + LOGGER.info("Exporting preventive action application report of basecase {} to file {}", + basecase, folder + File.separator + POST_PREVENTIVE_ACTIONS_FILE); + Path violationsPath = folder.resolve(POST_PREVENTIVE_ACTIONS_FILE); + Column[] COLUMNS = { + new Column("Basecase"), + new Column("ActionId"), + new Column("ViolatedEquipment"), + new Column("ViolationType"), + new Column("FailureStep"), + new Column("FailureDescription"), + new Column("ViolationRemoved"), + new Column("ActionApplied"), + new Column("Comment") + }; + CsvTableFormatterFactory factory = new CsvTableFormatterFactory(); + try (Writer writer = Files.newBufferedWriter(violationsPath, StandardCharsets.UTF_8); + TableFormatter formatter = factory.create(writer, POST_PREVENTIVE_ACTIONS_TITLE, TABLE_FORMATTER_CONFIG, COLUMNS)) { + writeActionsApplications(formatter, null, new ArrayList<>(preventiveActionsApplication.values())); + } + } + + private void exportPostPreventiveActionsViolationsWithUncertainties(Path folder) throws IOException { + LOGGER.info("Exporting post preventive actions violations without uncertainties report of basecase {} to file {}", + basecase, folder + File.separator + POST_PREVENTIVE_ACTIONS_VIOLATIONS_WITH_UNCERTAINTIES_FILE); + exportViolations(folder, + POST_PREVENTIVE_ACTIONS_VIOLATIONS_WITH_UNCERTAINTIES_FILE, + POST_PREVENTIVE_ACTIONS_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, + null, + postPreventiveActionsViolationsWithUncertainties); + } + + private void exportSecurityRulesApplication(Path folder) throws IOException { + LOGGER.info("Exporting security rules application report of basecase {} to file {}", + basecase, folder + File.separator + SECURITY_RULES_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); + Path violationsPath = folder.resolve(SECURITY_RULES_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); + Column[] COLUMNS = { + new Column("Basecase"), + new Column("ContingencyId"), + new Column("SecurityRule"), + new Column("WorkflowId"), + new Column("RuleViolated"), + new Column("ViolationType"), + new Column("Cause") + }; + CsvTableFormatterFactory factory = new CsvTableFormatterFactory(); + try (Writer writer = Files.newBufferedWriter(violationsPath, StandardCharsets.UTF_8); + TableFormatter formatter = factory.create(writer, SECURITY_RULES_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, TABLE_FORMATTER_CONFIG, COLUMNS)) { + if ( !securityRulesApplication.isEmpty() ) { + securityRulesApplication.forEach( ruleApplication -> { + try { + formatter.writeCell(basecase) + .writeCell(ruleApplication.getContingencyId()); + if ( ruleApplication.getSecurityRule() != null ) { + formatter.writeCell(ruleApplication.getSecurityRule().getId().toString()) + .writeCell(ruleApplication.getSecurityRule().getWorkflowId()); + } else { + formatter.writeEmptyCell() + .writeEmptyCell(); + } + formatter.writeCell(ruleApplication.isRuleViolated()) + .writeCell(ruleApplication.getRuleViolationType().name()); + if ( ruleApplication.getCause() != null ) { + formatter.writeCell(ruleApplication.getCause()); + } else { + formatter.writeEmptyCell(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + } + } + + private void exportPostContingencyViolations(Path folder) throws IOException { + LOGGER.info("Exporting post-contingency violations without uncertainties report of basecase {} to file {}", + basecase, folder + File.separator + POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); + LOGGER.info("Exporting post-contingency violations with uncertainties report of basecase {} to file {}", + basecase, folder + File.separator + POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE); + Path violationsPath1 = folder.resolve(POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); + Path violationsPath2 = folder.resolve(POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE); + Column[] COLUMNS = { + new Column("Basecase"), + new Column("Contingency"), + new Column("FailureStep"), + new Column("FailureDescription"), + new Column("ViolationType"), + new Column("Equipment"), + new Column("Value"), + new Column("Limit"), + new Column("Country"), + new Column("BaseVoltage") + }; + CsvTableFormatterFactory factory = new CsvTableFormatterFactory(); + try (Writer writer1 = Files.newBufferedWriter(violationsPath1, StandardCharsets.UTF_8); + TableFormatter formatter1 = factory.create(writer1, POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, TABLE_FORMATTER_CONFIG, COLUMNS); + Writer writer2 = Files.newBufferedWriter(violationsPath2, StandardCharsets.UTF_8); + TableFormatter formatter2 = factory.create(writer2, POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, TABLE_FORMATTER_CONFIG, COLUMNS)) { + if ( !postContingenciesStatus.isEmpty() ) { + postContingenciesStatus.forEach( postContingencyStatus -> { + // post-contingency violations without uncertainties + writeViolations(formatter1, + postContingencyStatus.getContingencyId(), + postContingencyStatus.getPostContingencyLoadflowResult(), + postContingencyStatus.getPostContingencyViolationsWithoutUncertainties()); + // post-contingency violations with uncertainties + writeViolations(formatter2, + postContingencyStatus.getContingencyId(), + postContingencyStatus.getPostContingencyWithUncertaintiesLoadflowResult(), + postContingencyStatus.getPostContingencyViolationsWithUncertainties()); + }); + } + } + } + + private void exportCurativeActionsApplication(Path folder) throws IOException { + LOGGER.info("Exporting curative action application report of basecase {} to file {}", + basecase, folder + File.separator + POST_CURATIVE_ACTIONS_FILE); + Path violationsPath = folder.resolve(POST_CURATIVE_ACTIONS_FILE); + Column[] COLUMNS = { + new Column("Basecase"), + new Column("Contingency"), + new Column("ActionId"), + new Column("ViolatedEquipment"), + new Column("ViolationType"), + new Column("FailureStep"), + new Column("FailureDescription"), + new Column("ViolationRemoved"), + new Column("ActionApplied"), + new Column("Comment") + }; + CsvTableFormatterFactory factory = new CsvTableFormatterFactory(); + try (Writer writer = Files.newBufferedWriter(violationsPath, StandardCharsets.UTF_8); + TableFormatter formatter = factory.create(writer, POST_CURATIVE_ACTIONS_TITLE, TABLE_FORMATTER_CONFIG, COLUMNS)) { + if ( !postContingenciesStatus.isEmpty() ) { + postContingenciesStatus.forEach( postContingencyStatus -> { + writeActionsApplications(formatter, + postContingencyStatus.getContingencyId(), + postContingencyStatus.getCurativeActionsApplication()); + }); + } + } + } + +} diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/SimpleContingencyDbFacadeTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/SimpleContingencyDbFacadeTest.java new file mode 100644 index 00000000..2bf1b934 --- /dev/null +++ b/wca-integration/src/test/java/eu/itesla_project/wca/SimpleContingencyDbFacadeTest.java @@ -0,0 +1,293 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +import static org.junit.Assert.assertEquals; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +import com.google.common.collect.ImmutableMap; + +import eu.itesla_project.contingency.Contingency; +import eu.itesla_project.contingency.ContingencyImpl; +import eu.itesla_project.contingency.LineContingency; +import eu.itesla_project.iidm.network.Country; +import eu.itesla_project.iidm.network.Line; +import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.iidm.network.NetworkFactory; +import eu.itesla_project.iidm.network.TopologyKind; +import eu.itesla_project.modules.contingencies.Action; +import eu.itesla_project.modules.contingencies.ActionPlan; +import eu.itesla_project.modules.contingencies.ActionPlanOption; +import eu.itesla_project.modules.contingencies.ActionsContingenciesAssociation; +import eu.itesla_project.modules.contingencies.Constraint; +import eu.itesla_project.modules.contingencies.ConstraintType; +import eu.itesla_project.modules.contingencies.ContingenciesAndActionsDatabaseClient; +import eu.itesla_project.modules.contingencies.SwitchClosingAction; +import eu.itesla_project.modules.contingencies.UnaryOperator; +import eu.itesla_project.modules.contingencies.impl.ActionImpl; +import eu.itesla_project.modules.contingencies.impl.ActionPlanImpl; +import eu.itesla_project.modules.contingencies.impl.ActionsContingenciesAssociationImpl; +import eu.itesla_project.modules.contingencies.impl.ConstraintImpl; +import eu.itesla_project.modules.contingencies.impl.LogicalExpressionImpl; +import eu.itesla_project.modules.contingencies.impl.OptionImpl; +import eu.itesla_project.security.LimitViolation; +import eu.itesla_project.security.LimitViolationType; +import eu.itesla_project.wca.ContingencyDbFacade; +import eu.itesla_project.wca.SimpleContingencyDbFacade; + +/** + * + * @author Massimo Ferraro + */ +@RunWith(MockitoJUnitRunner.class) +public class SimpleContingencyDbFacadeTest { + + @Test + public void testGetContingencies() throws Exception { + + // network + Network mockNetwork = NetworkFactory.create("mockNetwork", "test"); + // contingency + ContingencyImpl mockContingency = new ContingencyImpl("mockContingency", new LineContingency("mockLine")); + List mockContingencies = Arrays.asList(mockContingency); + // contingency and actions db client + ContingenciesAndActionsDatabaseClient mockContingenciesActionsDbClient = Mockito.mock(ContingenciesAndActionsDatabaseClient.class); + Mockito.when(mockContingenciesActionsDbClient.getContingencies(mockNetwork)).thenReturn(mockContingencies); + + ContingencyDbFacade contingencyDbFacade = new SimpleContingencyDbFacade(mockContingenciesActionsDbClient, mockNetwork); + assertEquals(mockContingencies, contingencyDbFacade.getContingencies()); + + } + + @Test + public void testGetCurativeActions() throws Exception { + + // network + Network mockNetwork = NetworkFactory.create("mockNetwork", "test"); + // contingency + String mockContingencyId = "mockContingency"; + ContingencyImpl mockContingency = new ContingencyImpl(mockContingencyId, new LineContingency("mockLine")); + // action + String mockActionId = "mockAction"; + Action mockAction = new ActionImpl(mockActionId, true, true, new SwitchClosingAction("mockVoltageLevel", "mockSwitch")); + // constraint + Constraint mockConstraint = new ConstraintImpl("mockline", 0, ConstraintType.BRANCH_OVERLOAD); + // association + ActionsContingenciesAssociation mockAssociation = new ActionsContingenciesAssociationImpl( + Arrays.asList(mockContingencyId), + Arrays.asList(mockConstraint), + Arrays.asList(mockActionId)); + // contingency and actions db client + ContingenciesAndActionsDatabaseClient mockContingenciesActionsDbClient = Mockito.mock(ContingenciesAndActionsDatabaseClient.class); + Mockito.when(mockContingenciesActionsDbClient.getActionsCtgAssociations(mockNetwork)).thenReturn(Arrays.asList(mockAssociation)); + Mockito.when(mockContingenciesActionsDbClient.getAction(mockActionId, mockNetwork)).thenReturn(mockAction); + + ContingencyDbFacade contingencyDbFacade = new SimpleContingencyDbFacade(mockContingenciesActionsDbClient, mockNetwork); + assertEquals(Arrays.asList(Collections.singletonList(mockAction)), contingencyDbFacade.getCurativeActions(mockContingency, null)); + + } + + @Test + public void testGetCurativeActionsWithActionPlans() throws Exception { + + // network + Network mockNetwork = NetworkFactory.create("mockNetwork", "test"); + // contingency + String mockContingencyId = "mockContingency"; + ContingencyImpl mockContingency = new ContingencyImpl(mockContingencyId, new LineContingency("mockLine")); + // action + String mockActionId = "mockAction"; + Action mockAction = new ActionImpl(mockActionId, true, true, new SwitchClosingAction("mockVoltageLevel", "mockSwitch")); + // action plan + Map mockOptionActions = ImmutableMap.of(new BigInteger("1"), mockActionId); + LogicalExpressionImpl mockLogicalExpression = new LogicalExpressionImpl(); + mockLogicalExpression.setOperator(new UnaryOperator(mockActionId)); + ActionPlanOption mockActionPlanOption = new OptionImpl(new BigInteger("1"), mockLogicalExpression, mockOptionActions); + Map mockPriorityOptions = ImmutableMap.of(new BigInteger("1"), mockActionPlanOption); + String mockActionPlanId = "mockActionPlan"; + ActionPlan mockActionPlan = new ActionPlanImpl(mockActionPlanId, "", Collections.emptyList(), mockPriorityOptions); + // constraint + Constraint mockConstraint = new ConstraintImpl("mockline", 0, ConstraintType.BRANCH_OVERLOAD); + // association + ActionsContingenciesAssociation mockAssociation = new ActionsContingenciesAssociationImpl( + Arrays.asList(mockContingencyId), + Arrays.asList(mockConstraint), + Arrays.asList(mockActionPlanId)); + // contingency and actions db client + ContingenciesAndActionsDatabaseClient mockContingenciesActionsDbClient = Mockito.mock(ContingenciesAndActionsDatabaseClient.class); + Mockito.when(mockContingenciesActionsDbClient.getActionsCtgAssociations(mockNetwork)).thenReturn(Arrays.asList(mockAssociation)); + Mockito.when(mockContingenciesActionsDbClient.getAction(mockActionPlanId, mockNetwork)).thenReturn(null); + Mockito.when(mockContingenciesActionsDbClient.getActionPlan(mockActionPlanId)).thenReturn(mockActionPlan); + Mockito.when(mockContingenciesActionsDbClient.getAction(mockActionId, mockNetwork)).thenReturn(mockAction); + + ContingencyDbFacade contingencyDbFacade = new SimpleContingencyDbFacade(mockContingenciesActionsDbClient, mockNetwork); + assertEquals(Arrays.asList(Collections.singletonList(mockAction)), contingencyDbFacade.getCurativeActions(mockContingency, null)); + + } + + @Test + public void testGetCurativeActionsWithViolations() throws Exception { + + // network + Network mockNetwork = NetworkFactory.create("mockNetwork", "test"); + // line + String mockLineId = "mockline"; + mockNetwork.newSubstation() + .setId("mockSubstation1") + .setCountry(Country.FR) + .add() + .newVoltageLevel() + .setId("mockVoltageLevel1") + .setNominalV(380) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .setHighVoltageLimit(400) + .setLowVoltageLimit(300) + .add() + .getBusBreakerView() + .newBus() + .setId("mockBus1") + .add(); + mockNetwork.newSubstation() + .setId("mockSubstation2") + .setCountry(Country.FR) + .add() + .newVoltageLevel() + .setId("mockVoltageLevel2") + .setNominalV(380) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .setHighVoltageLimit(400) + .setLowVoltageLimit(300) + .add().getBusBreakerView() + .newBus() + .setId("mockBus2") + .add(); + Line mockLine = mockNetwork.newLine() + .setId(mockLineId) + .setVoltageLevel1("mockVoltageLevel1") + .setBus1("mockBus1") + .setConnectableBus1("mockBus1") + .setVoltageLevel2("mockVoltageLevel2") + .setBus2("mockBus2") + .setConnectableBus2("mockBus2") + .setR(0) + .setX(0) + .setG1(0) + .setB1(0) + .setG2(0) + .setB2(0) + .add(); + // limit violation + LimitViolation mockLimitViolation = new LimitViolation(mockLineId, LimitViolationType.CURRENT, Float.NaN, null, Float.NaN); + List mockLimitViolations = Arrays.asList(mockLimitViolation); + // contingency + String mockContingencyId = "mockContingency"; + ContingencyImpl mockContingency = new ContingencyImpl(mockContingencyId, new LineContingency("mockLine")); + // action + String mockActionId = "mockAction"; + Action mockAction = new ActionImpl(mockActionId, true, true, new SwitchClosingAction("mockVoltageLevel", "mockSwitch")); + // constraint + Constraint mockConstraint = new ConstraintImpl(mockLineId, 0, ConstraintType.BRANCH_OVERLOAD); + // association + ActionsContingenciesAssociation mockAssociation = new ActionsContingenciesAssociationImpl( + Arrays.asList(mockContingencyId), + Arrays.asList(mockConstraint), + Arrays.asList(mockActionId)); + // contingency and actions db client + ContingenciesAndActionsDatabaseClient mockContingenciesActionsDbClient = Mockito.mock(ContingenciesAndActionsDatabaseClient.class); + Mockito.when(mockContingenciesActionsDbClient.getActionsCtgAssociations(mockNetwork)).thenReturn(Arrays.asList(mockAssociation)); + Mockito.when(mockContingenciesActionsDbClient.getAction(mockActionId, mockNetwork)).thenReturn(mockAction); + + ContingencyDbFacade contingencyDbFacade = new SimpleContingencyDbFacade(mockContingenciesActionsDbClient, mockNetwork); + assertEquals(Arrays.asList(Collections.singletonList(mockAction)), contingencyDbFacade.getCurativeActions(mockContingency, mockLimitViolations)); + + } + + @Test + public void testGetPreventiveActions() throws Exception { + + // network + Network mockNetwork = NetworkFactory.create("mockNetwork", "test"); + // line + String mockLineId = "mockline"; + mockNetwork.newSubstation() + .setId("mockSubstation1") + .setCountry(Country.FR) + .add() + .newVoltageLevel() + .setId("mockVoltageLevel1") + .setNominalV(380) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .setHighVoltageLimit(400) + .setLowVoltageLimit(300) + .add() + .getBusBreakerView() + .newBus() + .setId("mockBus1") + .add(); + mockNetwork.newSubstation() + .setId("mockSubstation2") + .setCountry(Country.FR) + .add() + .newVoltageLevel() + .setId("mockVoltageLevel2") + .setNominalV(380) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .setHighVoltageLimit(400) + .setLowVoltageLimit(300) + .add().getBusBreakerView() + .newBus() + .setId("mockBus2") + .add(); + Line mockLine = mockNetwork.newLine() + .setId(mockLineId) + .setVoltageLevel1("mockVoltageLevel1") + .setBus1("mockBus1") + .setConnectableBus1("mockBus1") + .setVoltageLevel2("mockVoltageLevel2") + .setBus2("mockBus2") + .setConnectableBus2("mockBus2") + .setR(0) + .setX(0) + .setG1(0) + .setB1(0) + .setG2(0) + .setB2(0) + .add(); + // limit violation + LimitViolation mockLimitViolation = new LimitViolation(mockLineId, LimitViolationType.CURRENT, Float.NaN, null, Float.NaN); + // action + String mockActionId = "mockAction"; + Action mockAction = new ActionImpl(mockActionId, true, true, new SwitchClosingAction("mockVoltageLevel", "mockSwitch")); + // constraint + Constraint mockConstraint = new ConstraintImpl(mockLineId, 0, ConstraintType.BRANCH_OVERLOAD); + // association + ActionsContingenciesAssociation mockAssociation = new ActionsContingenciesAssociationImpl( + Collections.emptyList(), + Arrays.asList(mockConstraint), + Arrays.asList(mockActionId)); + // contingency and actions db client + ContingenciesAndActionsDatabaseClient mockContingenciesActionsDbClient = Mockito.mock(ContingenciesAndActionsDatabaseClient.class); + Mockito.when(mockContingenciesActionsDbClient.getActionsCtgAssociationsByConstraint(mockLineId, ConstraintType.BRANCH_OVERLOAD)) + .thenReturn(Arrays.asList(mockAssociation)); + Mockito.when(mockContingenciesActionsDbClient.getAction(mockActionId, mockNetwork)).thenReturn(mockAction); + + ContingencyDbFacade contingencyDbFacade = new SimpleContingencyDbFacade(mockContingenciesActionsDbClient, mockNetwork); + assertEquals(Arrays.asList(Collections.singletonList(mockAction)), contingencyDbFacade.getPreventiveActions(mockLimitViolation)); + + } + +} diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/UncertaintiesAmplWriterTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/UncertaintiesAmplWriterTest.java new file mode 100644 index 00000000..1e4a2f44 --- /dev/null +++ b/wca-integration/src/test/java/eu/itesla_project/wca/UncertaintiesAmplWriterTest.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +import static org.junit.Assert.assertEquals; + +import java.nio.charset.StandardCharsets; + +import org.joda.time.Interval; +import org.junit.Test; + +import eu.itesla_project.commons.util.StringToIntMapper; +import eu.itesla_project.iidm.datasource.MemDataSource; +import eu.itesla_project.iidm.export.ampl.AmplSubset; +import eu.itesla_project.iidm.export.ampl.AmplUtil; +import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.iidm.network.test.NetworkTest1Factory; +import eu.itesla_project.modules.wca.Uncertainties; +import eu.itesla_project.modules.wca.UncertaintiesAnalyser; +import eu.itesla_project.wca.uncertainties.UncertaintiesAmplWriter; +import eu.itesla_project.wca.uncertainties.UncertaintiesAnalyserTestImpl; + +/** + * + * @author Massimo Ferraro + */ +public class UncertaintiesAmplWriterTest { + + @Test + public void testWrite() throws Exception { + Network network = NetworkTest1Factory.create(); + Interval histoInterval = Interval.parse("2013-01-01T00:00:00+01:00/2013-01-31T23:59:00+01:00"); + + UncertaintiesAnalyser uncertaintiesAnalyser = new UncertaintiesAnalyserTestImpl(network); + Uncertainties uncertainties = uncertaintiesAnalyser.analyse(histoInterval).join(); + + MemDataSource dataSource = new MemDataSource(); + + StringToIntMapper mapper = new StringToIntMapper<>(AmplSubset.class); + AmplUtil.fillMapper(mapper, network); + + new UncertaintiesAmplWriter(uncertainties, dataSource, mapper).write(); + + String fileContent = String.join(System.lineSeparator(), + "#Reduction matrix", + "#\"inj. type\" \"inj. num\" \"var. num\" \"coeff.\"", + "L 1 1 1.00000", + "G 1 2 1.00000"); + assertEquals(fileContent, new String(dataSource.getData(WCAConstants.REDUCTION_MATRIX_FILE_SUFFIX, WCAConstants.TXT_EXT), StandardCharsets.UTF_8).trim()); + + fileContent = String.join(System.lineSeparator(), + "#Trust intervals", + "#\"var. num\" \"min\" \"max\"", + "1 -1.00000 1.00000", + "2 -90.0000 90.0000"); + assertEquals(fileContent, new String(dataSource.getData(WCAConstants.TRUST_INTERVAL_FILE_SUFFIX, WCAConstants.TXT_EXT), StandardCharsets.UTF_8).trim()); + + fileContent = String.join(System.lineSeparator(), + "#Means", + "#\"inj. type\" \"inj. num\" \"mean\"", + "L 1 10.0000", + "G 1 900.000"); + assertEquals(fileContent, new String(dataSource.getData(WCAConstants.MEANS_FILE_SUFFIX, WCAConstants.TXT_EXT), StandardCharsets.UTF_8).trim()); + } + +} diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/WCAConfigTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/WCAConfigTest.java new file mode 100644 index 00000000..c0d8e74c --- /dev/null +++ b/wca-integration/src/test/java/eu/itesla_project/wca/WCAConfigTest.java @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +import static org.junit.Assert.assertEquals; + +import java.nio.file.FileSystem; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Set; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; + +import eu.itesla_project.commons.config.InMemoryPlatformConfig; +import eu.itesla_project.commons.config.MapModuleConfig; +import eu.itesla_project.iidm.network.Country; + +/** + * + * @author Massimo Ferraro + */ +public class WCAConfigTest { + + private FileSystem fileSystem; + private InMemoryPlatformConfig platformConfig; + private MapModuleConfig moduleConfig; + private Path xpressHome; + + @Before + public void setUp() throws Exception { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + platformConfig = new InMemoryPlatformConfig(fileSystem); + moduleConfig = platformConfig.createModuleConfig("wca"); + xpressHome = fileSystem.getPath("/xpress-home"); + moduleConfig.setStringListProperty("xpressHome", Arrays.asList(xpressHome.toString())); + + } + + @After + public void tearDown() throws Exception { + fileSystem.close(); + } + + @Test + public void testBasicConfig() throws Exception { + WCAConfig config = WCAConfig.load(platformConfig); + + checkValues(config, xpressHome, WCAConfig.DEFAULT_REDUCED_VARIABLE_RATIO, WCAConfig.DEFAULT_DEBUG, WCAConfig.DEFAULT_EXPORT_STATE, + WCAConfig.DEFAULT_RESTRICTING_THRESHOLD_LEVELS, WCAConfig.DEFAULT_MARGIN, WCAConfig.DEFAULT_IGNORE_VOLTAGE_CONSTRAINTS, + WCAConfig.DEFAULT_ACTIVATE_FILTERING, WCAConfig.DEFAULT_PREVENTIVE_ACTIONS_FILTER, WCAConfig.DEFAULT_PREVENTIVE_ACTIONS_OPTIMIZER, + WCAConfig.DEFAULT_APPLY_PREVENTIVE_ACTIONS, WCAConfig.DEFAULT_CURATIVE_ACTIONS_OPTIMIZER, WCAConfig.DEFAULT_VOLTAGE_LEVEL_CONSTRAINTS_FILTER, + WCAConfig.DEFAULT_COUNTRY_CONSTRAINTS_FILTER, WCAConfig.DEFAULT_FILTER_PREVENTIVE_ACTIONS, WCAConfig.DEFAULT_FILTER_CURATIVE_ACTIONS, + WCAConfig.DEFAULT_LOOOSEN_CONSTRAINTS); + } + + @Test + public void testCompleteConfig() throws Exception { + float reducedVariableRatio = 0; + boolean debug = true; + boolean exportStates = true; + float margin = 1; + Set restrictingThresholdLevels = EnumSet.of(WCARestrictingThresholdLevel.NO_FOREIGN_THRESHOLDS); + boolean ignoreVoltageConstraints = true; + boolean activateFiltering = true; + WCAPreventiveActionsFilter preventiveActionsFilter = WCAPreventiveActionsFilter.LF; + WCAPreventiveActionsOptimizer preventiveActionsOptimizer = WCAPreventiveActionsOptimizer.LF_HEURISTIC; + boolean applyPreventiveActions = true; + WCACurativeActionsOptimizer curativeActionsOptimizer = WCACurativeActionsOptimizer.LF_HEURISTIC; + float voltageLevelConstraintFilter = 400; + Set countryConstraintFilter = EnumSet.of(Country.FR); + boolean filterPreventiveActions = false; + boolean filterCurativeActions = false; + boolean loosenConstraints = true; + + moduleConfig.setStringListProperty("reducedVariableRatio", Arrays.asList(Float.toString(reducedVariableRatio))); + moduleConfig.setStringListProperty("debug", Arrays.asList(Boolean.toString(debug))); + moduleConfig.setStringListProperty("exportStates", Arrays.asList(Boolean.toString(exportStates))); + moduleConfig.setStringListProperty("restrictingThresholdLevels", Arrays.asList(WCARestrictingThresholdLevel.NO_FOREIGN_THRESHOLDS.name())); + moduleConfig.setStringListProperty("margin", Arrays.asList(Float.toString(margin))); + moduleConfig.setStringListProperty("ignoreVoltageConstraints", Arrays.asList(Boolean.toString(ignoreVoltageConstraints))); + moduleConfig.setStringListProperty("activateFiltering", Arrays.asList(Boolean.toString(activateFiltering))); + moduleConfig.setStringListProperty("preventiveActionsFilter", Arrays.asList(preventiveActionsFilter.name())); + moduleConfig.setStringListProperty("preventiveActionsOptimizer", Arrays.asList(preventiveActionsOptimizer.name())); + moduleConfig.setStringListProperty("applyPreventiveActions", Arrays.asList(Boolean.toString(applyPreventiveActions))); + moduleConfig.setStringListProperty("curativeActionsOptimizer", Arrays.asList(curativeActionsOptimizer.name())); + moduleConfig.setStringListProperty("voltageLevelConstraintFilter", Arrays.asList(Float.toString(voltageLevelConstraintFilter))); + moduleConfig.setStringListProperty("countryConstraintFilter", Arrays.asList(Country.FR.name())); + moduleConfig.setStringListProperty("filterPreventiveActions", Arrays.asList(Boolean.toString(filterPreventiveActions))); + moduleConfig.setStringListProperty("filterCurativeActions", Arrays.asList(Boolean.toString(filterCurativeActions))); + moduleConfig.setStringListProperty("loosenConstraints", Arrays.asList(Boolean.toString(loosenConstraints))); + + WCAConfig parameters = WCAConfig.load(platformConfig); + + checkValues(parameters, xpressHome, reducedVariableRatio, debug, exportStates, restrictingThresholdLevels, margin, + ignoreVoltageConstraints, activateFiltering, preventiveActionsFilter, preventiveActionsOptimizer, + applyPreventiveActions, curativeActionsOptimizer, voltageLevelConstraintFilter, countryConstraintFilter, + filterPreventiveActions, filterCurativeActions, loosenConstraints); + } + + private void checkValues(WCAConfig config, Path xpressHome, float reducedVariableRatio, boolean debug, boolean exportStates, + Set restrictingThresholdLevels, float margin, boolean ignoreVoltageConstraints, + boolean activateFiltering, WCAPreventiveActionsFilter preventiveActionsFilter, + WCAPreventiveActionsOptimizer preventiveActionsOptimizer, boolean applyPreventiveActions, + WCACurativeActionsOptimizer curativeActionsOptimizer, float voltageLevelConstraintFilter, + Set countryConstraintFilter, boolean filterPreventiveActions, boolean filterCurativeActions, + boolean loosenConstraints) { + assertEquals(xpressHome, config.getXpressHome()); + assertEquals(reducedVariableRatio, config.getReducedVariableRatio(), 0); + assertEquals(debug, config.isDebug()); + assertEquals(exportStates, config.isExportStates()); + assertEquals(restrictingThresholdLevels, config.getRestrictingThresholdLevels()); + assertEquals(margin, config.getMargin(), 0); + assertEquals(ignoreVoltageConstraints, config.ignoreVoltageConstraints()); + assertEquals(activateFiltering, config.activateFiltering()); + assertEquals(preventiveActionsFilter, config.getPreventiveActionsFilter()); + assertEquals(preventiveActionsOptimizer, config.getPreventiveActionsOptimizer()); + assertEquals(applyPreventiveActions, config.applyPreventiveActions()); + assertEquals(curativeActionsOptimizer, config.getCurativeActionsOptimizer()); + assertEquals(voltageLevelConstraintFilter, config.getVoltageLevelConstraintFilter(), 0); + assertEquals(countryConstraintFilter, config.getCountryConstraintFilter()); + assertEquals(filterPreventiveActions, config.filterPreventiveActions()); + assertEquals(filterCurativeActions, config.filterCurativeActions()); + assertEquals(loosenConstraints, config.loosenConstraints()); + } + +} \ No newline at end of file diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/WCAFilteredClustersTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/WCAFilteredClustersTest.java new file mode 100644 index 00000000..dd876a41 --- /dev/null +++ b/wca-integration/src/test/java/eu/itesla_project/wca/WCAFilteredClustersTest.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Collections; +import java.util.EnumSet; + +import org.junit.Test; + +import eu.itesla_project.modules.wca.WCAClusterNum; + +/** + * + * @author Massimo Ferraro + */ +public class WCAFilteredClustersTest { + + @Test + public void test() throws Exception { + String contingencyId = "contigencyId"; + WCAFilteredClusters filteredClusters = new WCAFilteredClusters("networkId", Collections.singletonList(contingencyId)); + checkValues(filteredClusters, + contingencyId, + WCAClusterNum.FOUR, + EnumSet.of(WCAClusterNum.ONE, WCAClusterNum.TWO, WCAClusterNum.THREE, WCAClusterNum.FOUR), + EnumSet.noneOf(WCAClusterOrigin.class)); + + filteredClusters.removeClusters(contingencyId, EnumSet.of(WCAClusterNum.ONE,WCAClusterNum.FOUR), WCAClusterOrigin.LF_BASIC_VIOLATION); + checkValues(filteredClusters, + contingencyId, + WCAClusterNum.THREE, + EnumSet.of(WCAClusterNum.TWO, WCAClusterNum.THREE), + EnumSet.of(WCAClusterOrigin.LF_BASIC_VIOLATION)); + + filteredClusters.addClusters(contingencyId, EnumSet.of(WCAClusterNum.FOUR), WCAClusterOrigin.CLUSTERS_ANALYSIS); + checkValues(filteredClusters, + contingencyId, + WCAClusterNum.FOUR, + EnumSet.of(WCAClusterNum.TWO, WCAClusterNum.THREE, WCAClusterNum.FOUR), + EnumSet.of(WCAClusterOrigin.LF_BASIC_VIOLATION, WCAClusterOrigin.CLUSTERS_ANALYSIS)); + + filteredClusters.removeClusters(contingencyId, EnumSet.of(WCAClusterNum.TWO,WCAClusterNum.THREE,WCAClusterNum.FOUR), WCAClusterOrigin.LF_DIVERGENCE); + checkValues(filteredClusters, + contingencyId, + WCAClusterNum.UNDEFINED, + EnumSet.noneOf(WCAClusterNum.class), + EnumSet.of(WCAClusterOrigin.LF_BASIC_VIOLATION, WCAClusterOrigin.CLUSTERS_ANALYSIS, WCAClusterOrigin.LF_DIVERGENCE)); + + filteredClusters.addClusters(contingencyId, EnumSet.of(WCAClusterNum.ONE, WCAClusterNum.TWO, WCAClusterNum.THREE, WCAClusterNum.FOUR), WCAClusterOrigin.LF_RULE_VIOLATION); + checkValues(filteredClusters, + contingencyId, + WCAClusterNum.FOUR, + EnumSet.of(WCAClusterNum.ONE, WCAClusterNum.TWO, WCAClusterNum.THREE, WCAClusterNum.FOUR), + EnumSet.of(WCAClusterOrigin.LF_BASIC_VIOLATION, WCAClusterOrigin.CLUSTERS_ANALYSIS, WCAClusterOrigin.LF_DIVERGENCE, WCAClusterOrigin.LF_RULE_VIOLATION)); + + filteredClusters.removeAllButCluster(contingencyId, WCAClusterNum.TWO, WCAClusterOrigin.LF_DIVERGENCE); + checkValues(filteredClusters, + contingencyId, + WCAClusterNum.TWO, + EnumSet.of(WCAClusterNum.TWO), + EnumSet.of(WCAClusterOrigin.LF_BASIC_VIOLATION, WCAClusterOrigin.CLUSTERS_ANALYSIS, WCAClusterOrigin.LF_DIVERGENCE, WCAClusterOrigin.LF_RULE_VIOLATION)); + + filteredClusters.removeAllButCluster(contingencyId, WCAClusterNum.ONE, WCAClusterOrigin.CLUSTERS_ANALYSIS); + checkValues(filteredClusters, + contingencyId, + WCAClusterNum.TWO, + EnumSet.of(WCAClusterNum.TWO), + EnumSet.of(WCAClusterOrigin.LF_BASIC_VIOLATION, WCAClusterOrigin.CLUSTERS_ANALYSIS, WCAClusterOrigin.LF_DIVERGENCE, WCAClusterOrigin.LF_RULE_VIOLATION)); + } + + private void checkValues(WCAFilteredClusters filteredClusters, String contingencyId, WCAClusterNum expectedClusterNum, + EnumSet expectedClusterNums, EnumSet expectedFlags) { + + assertEquals(expectedClusterNum, filteredClusters.getCluster(contingencyId)); + + if ( !expectedClusterNum.equals(WCAClusterNum.UNDEFINED) ) { + assertTrue(filteredClusters.hasCluster(contingencyId, expectedClusterNum)); + } + + assertEquals(expectedClusterNums, filteredClusters.getClusters(contingencyId)); + + assertEquals(expectedFlags, filteredClusters.getFlags(contingencyId)); + } + +} diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/WCAHistoLimitsTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/WCAHistoLimitsTest.java new file mode 100644 index 00000000..791c9443 --- /dev/null +++ b/wca-integration/src/test/java/eu/itesla_project/wca/WCAHistoLimitsTest.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.joda.time.Interval; +import org.junit.Test; +import org.mockito.Matchers; +import org.mockito.Mockito; + +import eu.itesla_project.commons.util.StringToIntMapper; +import eu.itesla_project.iidm.datasource.MemDataSource; +import eu.itesla_project.iidm.export.ampl.AmplSubset; +import eu.itesla_project.iidm.export.ampl.AmplUtil; +import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.iidm.network.test.NetworkTest1Factory; +import eu.itesla_project.modules.histo.HistoDbAttr; +import eu.itesla_project.modules.histo.HistoDbClient; +import eu.itesla_project.modules.histo.HistoDbHorizon; +import eu.itesla_project.modules.histo.HistoDbNetworkAttributeId; +import eu.itesla_project.modules.histo.HistoDbStats; +import eu.itesla_project.modules.histo.HistoDbStatsType; + +/** + * + * @author Massimo Ferraro + */ +public class WCAHistoLimitsTest { + + @Test + public void testWrite() throws IOException, InterruptedException { + Interval histoInterval = Interval.parse("2013-01-01T00:00:00+01:00/2013-01-31T23:59:00+01:00"); + + Network network = NetworkTest1Factory.create(); + + HistoDbClient histoDbClient = Mockito.mock(HistoDbClient.class); + HistoDbStats histoDbStats = new HistoDbStats(); + histoDbStats.setValue(HistoDbStatsType.MIN, new HistoDbNetworkAttributeId(network.getLoads().iterator().next().getId(), HistoDbAttr.P), 0f); + histoDbStats.setValue(HistoDbStatsType.MAX, new HistoDbNetworkAttributeId(network.getLoads().iterator().next().getId(), HistoDbAttr.P), 20f); + histoDbStats.setValue(HistoDbStatsType.MIN, new HistoDbNetworkAttributeId(network.getGenerators().iterator().next().getId(), HistoDbAttr.P), 200f); + histoDbStats.setValue(HistoDbStatsType.MAX, new HistoDbNetworkAttributeId(network.getGenerators().iterator().next().getId(), HistoDbAttr.P), 900f); + Mockito.when(histoDbClient.queryStats(Matchers.anySet(), Matchers.eq(histoInterval), Matchers.eq(HistoDbHorizon.SN), Matchers.eq(true))) + .thenReturn(histoDbStats); + + MemDataSource dataSource = new MemDataSource(); + + StringToIntMapper mapper = new StringToIntMapper<>(AmplSubset.class); + AmplUtil.fillMapper(mapper, network); + + WCAHistoLimits histoLimits = new WCAHistoLimits(histoInterval); + histoLimits.load(network, histoDbClient); + histoLimits.write(dataSource, mapper); + + String fileContent = String.join(System.lineSeparator(), + "#loads historical data " + histoInterval, + "#\"num\" \"min p (MW)\" \"max p (MW)\" \"id\"", + "1 0.00000 20.0000 \""+ network.getLoads().iterator().next().getId() + "\""); + assertEquals(fileContent, new String(dataSource.getData(WCAConstants.HISTO_LOADS_FILE_SUFFIX, WCAConstants.TXT_EXT), StandardCharsets.UTF_8).trim()); + + fileContent = String.join(System.lineSeparator(), + "#generators historical data " + histoInterval, + "#\"num\" \"min p (MW)\" \"max p (MW)\" \"id\"", + "1 200.000 900.000 \""+ network.getGenerators().iterator().next().getId() + "\""); + assertEquals(fileContent, new String(dataSource.getData(WCAConstants.HISTO_GENERATORS_FILE_SUFFIX, WCAConstants.TXT_EXT), StandardCharsets.UTF_8).trim()); + } + +} diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/WCAImplTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/WCAImplTest.java new file mode 100644 index 00000000..45f09b2f --- /dev/null +++ b/wca-integration/src/test/java/eu/itesla_project/wca/WCAImplTest.java @@ -0,0 +1,462 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +import org.joda.time.Interval; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Matchers; +import org.mockito.Mockito; + +import eu.itesla_project.computation.ComputationManager; +import eu.itesla_project.computation.ExecutionEnvironment; +import eu.itesla_project.contingency.Contingency; +import eu.itesla_project.contingency.tasks.ModificationTask; +import eu.itesla_project.iidm.network.Bus; +import eu.itesla_project.iidm.network.Country; +import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.iidm.network.StateManager; +import eu.itesla_project.iidm.network.test.EurostagTutorialExample1Factory; +import eu.itesla_project.loadflow.api.LoadFlow; +import eu.itesla_project.loadflow.api.LoadFlowFactory; +import eu.itesla_project.loadflow.api.LoadFlowParameters; +import eu.itesla_project.loadflow.api.LoadFlowResult; +import eu.itesla_project.modules.contingencies.Action; +import eu.itesla_project.modules.contingencies.ActionsContingenciesAssociation; +import eu.itesla_project.modules.contingencies.Constraint; +import eu.itesla_project.modules.contingencies.ConstraintType; +import eu.itesla_project.modules.contingencies.ContingenciesAndActionsDatabaseClient; +import eu.itesla_project.modules.contingencies.impl.ActionsContingenciesAssociationImpl; +import eu.itesla_project.modules.contingencies.impl.ConstraintImpl; +import eu.itesla_project.modules.histo.HistoDbAttr; +import eu.itesla_project.modules.histo.HistoDbClient; +import eu.itesla_project.modules.histo.HistoDbHorizon; +import eu.itesla_project.modules.histo.HistoDbNetworkAttributeId; +import eu.itesla_project.modules.histo.HistoDbStats; +import eu.itesla_project.modules.histo.HistoDbStatsType; +import eu.itesla_project.modules.rules.RuleAttributeSet; +import eu.itesla_project.modules.rules.RuleId; +import eu.itesla_project.modules.rules.RulesDbClient; +import eu.itesla_project.modules.rules.SecurityRule; +import eu.itesla_project.modules.rules.SecurityRuleExpression; +import eu.itesla_project.modules.rules.SecurityRuleStatus; +import eu.itesla_project.modules.rules.expr.AndOperator; +import eu.itesla_project.modules.rules.expr.Attribute; +import eu.itesla_project.modules.rules.expr.ComparisonOperator; +import eu.itesla_project.modules.rules.expr.ExpressionNode; +import eu.itesla_project.modules.rules.expr.Litteral; +import eu.itesla_project.modules.wca.UncertaintiesAnalyserFactory; +import eu.itesla_project.modules.wca.WCA; +import eu.itesla_project.modules.wca.WCAClusterNum; +import eu.itesla_project.modules.wca.WCAParameters; +import eu.itesla_project.modules.wca.WCAResult; +import eu.itesla_project.modules.wca.report.WCAActionApplication; +import eu.itesla_project.modules.wca.report.WCAPostContingencyStatus; +import eu.itesla_project.modules.wca.report.WCAReport; +import eu.itesla_project.modules.wca.report.WCARuleViolationType; +import eu.itesla_project.security.LimitViolationType; +import eu.itesla_project.simulation.securityindexes.SecurityIndexId; +import eu.itesla_project.simulation.securityindexes.SecurityIndexType; +import eu.itesla_project.wca.uncertainties.UncertaintiesAnalyserFactoryTestImpl; + +/** + * + * @author Massimo Ferraro + */ +public class WCAImplTest { + + private Interval histoInterval; + private Contingency contingency; + private Action action; + private Network network; + private ComputationManager computationManager; + private HistoDbClient histoDbClient; + private RulesDbClient rulesDbClient; + private ContingenciesAndActionsDatabaseClient contingenciesActionsDbClient; + private UncertaintiesAnalyserFactory uncertaintiesAnalyserFactory; + private LoadFlowFactory loadFlowFactory; + + @Before + public void setUp() throws Exception { + histoInterval = Interval.parse("2013-01-01T00:00:00+01:00/2013-01-31T23:59:00+01:00"); + + network = EurostagTutorialExample1Factory.create(); + ((Bus) network.getIdentifiable("NHV1")).setV(380f); + ((Bus) network.getIdentifiable("NHV2")).setV(380f); + network.getLine("NHV1_NHV2_1").getTerminal1().setP(560f).setQ(550f); + network.getLine("NHV1_NHV2_1").getTerminal2().setP(560f).setQ(550f); + network.getLine("NHV1_NHV2_1").newCurrentLimits1().setPermanentLimit(1500f).add(); + network.getLine("NHV1_NHV2_1").newCurrentLimits2().setPermanentLimit(1500f).add(); + network.getLine("NHV1_NHV2_2").getTerminal1().setP(560f).setQ(550f); + network.getLine("NHV1_NHV2_2").getTerminal2().setP(560f).setQ(550f); + network.getLine("NHV1_NHV2_2").newCurrentLimits1().setPermanentLimit(1500f).add(); + network.getLine("NHV1_NHV2_2").newCurrentLimits2().setPermanentLimit(1500f).add(); + network.getStateManager().allowStateMultiThreadAccess(true); + + computationManager = Mockito.mock(ComputationManager.class); + Executor executor = new Executor() { + @Override + public void execute(Runnable r) { + r.run(); + } + }; + Mockito.when(computationManager.getExecutor()).thenReturn(executor); + + histoDbClient = Mockito.mock(HistoDbClient.class); + HistoDbStats histoDbStats = new HistoDbStats(); + network.getLoads().forEach( load -> { + histoDbStats.setValue(HistoDbStatsType.MIN, new HistoDbNetworkAttributeId(load.getId(), HistoDbAttr.P), load.getP0() - (load.getP0()*20/110)); + histoDbStats.setValue(HistoDbStatsType.MAX, new HistoDbNetworkAttributeId(load.getId(), HistoDbAttr.P), load.getP0() + (load.getP0()*20/110)); + }); + network.getGenerators().forEach( generator -> { + histoDbStats.setValue(HistoDbStatsType.MIN, new HistoDbNetworkAttributeId(generator.getId(), HistoDbAttr.P), generator.getTargetP() - (generator.getTargetP()*20/110)); + histoDbStats.setValue(HistoDbStatsType.MAX, new HistoDbNetworkAttributeId(generator.getId(), HistoDbAttr.P), generator.getTargetP() + (generator.getTargetP()*20/110)); + }); + Mockito.when(histoDbClient.queryStats(Matchers.anySet(), Matchers.eq(histoInterval), Matchers.eq(HistoDbHorizon.SN), Matchers.eq(true))) + .thenReturn(histoDbStats); + + rulesDbClient = Mockito.mock(RulesDbClient.class); + + contingenciesActionsDbClient = Mockito.mock(ContingenciesAndActionsDatabaseClient.class); + contingency = Mockito.mock(Contingency.class); + Mockito.when(contingency.getId()).thenReturn("NHV1_NHV2_1_contingency"); + Mockito.when(contingency.toTask()).thenReturn(new ModificationTask() { + @Override + public void modify(Network network, ComputationManager computationManager) { + network.getLine("NHV1_NHV2_1").getTerminal1().disconnect(); + network.getLine("NHV1_NHV2_1").getTerminal2().disconnect(); + network.getLine("NHV1_NHV2_2").getTerminal1().setP(860f); + } + }); + action = Mockito.mock(Action.class); + Mockito.when(action.getId()).thenReturn("NHV1_NHV2_2_curative_action"); + Mockito.when(action.toTask()).thenReturn(new ModificationTask() { + @Override + public void modify(Network network, ComputationManager computationManager) { + network.getLine("NHV1_NHV2_1").getTerminal1().connect(); + network.getLine("NHV1_NHV2_1").getTerminal2().connect(); + network.getLine("NHV1_NHV2_2").getTerminal1().setP(560f); + } + }); + Constraint constraint = new ConstraintImpl("NHV1_NHV2_2", 0, ConstraintType.BRANCH_OVERLOAD); + ActionsContingenciesAssociation association = new ActionsContingenciesAssociationImpl( + Arrays.asList(contingency.getId()), + Arrays.asList(constraint), + Arrays.asList(action.getId())); + Mockito.when(contingenciesActionsDbClient.getContingencies(network)).thenReturn(Arrays.asList(contingency)); + Mockito.when(contingenciesActionsDbClient.getAction(action.getId(), network)).thenReturn(action); + Mockito.when(contingenciesActionsDbClient.getActionsCtgAssociations(network)).thenReturn(Arrays.asList(association)); + + uncertaintiesAnalyserFactory = new UncertaintiesAnalyserFactoryTestImpl(); + + loadFlowFactory = Mockito.mock(LoadFlowFactory.class); + LoadFlowResult loadFlowResult = new LoadFlowResult() { + @Override + public boolean isOk() { + return true; + } + + @Override + public Map getMetrics() { + return Collections.emptyMap(); + } + + @Override + public String getLogs() { + return null; + } + }; + LoadFlow loadFlow = Mockito.mock(LoadFlow.class); + Mockito.when(loadFlow.run(Matchers.any(LoadFlowParameters.class))).thenReturn(loadFlowResult); + Mockito.when(loadFlow.runAsync(Matchers.any(String.class), Matchers.any(LoadFlowParameters.class))) + .thenReturn(CompletableFuture.completedFuture(loadFlowResult)); + Mockito.when(loadFlowFactory.create(network, computationManager, 0)).thenReturn(loadFlow); + } + + @Test + public void testRunWithCurativeActions() throws Exception { + WCAClustersResult clustersResult = new WCAClustersResult(WCAClusterNum.TWO, true, 1, Collections.emptyMap()); + Mockito.when(computationManager.execute(Mockito.any(ExecutionEnvironment.class), Mockito.any())) + .thenReturn(CompletableFuture.completedFuture(clustersResult)); + + WCAConfig config = new WCAConfig(Paths.get("/xpress-home"), 1f, true, false, EnumSet.noneOf(WCARestrictingThresholdLevel.class), + 0f, true, false, WCAPreventiveActionsFilter.LF, WCAPreventiveActionsOptimizer.NONE, false, + WCACurativeActionsOptimizer.CLUSTERS, 0f, EnumSet.noneOf(Country.class), true, true, false); + WCAParameters parameters = new WCAParameters(histoInterval, null, null, 1); + WCA wca = new WCAImpl(network, computationManager, histoDbClient, rulesDbClient, uncertaintiesAnalyserFactory, + contingenciesActionsDbClient, loadFlowFactory, config); + WCAResult result = wca.run(parameters); + WCAReport report = wca.getReport(); + + assertEquals(WCAClusterNum.TWO, result.getClusters().iterator().next().getNum()); + assertTrue(result.getClusters().iterator().next().getOrigin().contains(WCAClusterOrigin.LF_POST_CONTINGENCY_VIOLATION.name())); + assertTrue(result.getClusters().iterator().next().getOrigin().contains(WCAClusterOrigin.CLUSTERS_ANALYSIS.name())); + + assertTrue(report.getBaseStateLoadflowResult().loadflowConverged()); + assertTrue(report.getPreContingencyViolationsWithoutUncertainties().isEmpty()); + assertTrue(report.getPreContingencyViolationsWithUncertainties().isEmpty()); + assertEquals(1, report.getPostContingenciesStatus().size()); + WCAPostContingencyStatus postContingencyStatus = report.getPostContingenciesStatus().iterator().next(); + assertEquals(contingency.getId(), postContingencyStatus.getContingencyId()); + assertEquals(1, postContingencyStatus.getPostContingencyViolationsWithoutUncertainties().size()); + assertEquals("NHV1_NHV2_2", postContingencyStatus.getPostContingencyViolationsWithoutUncertainties().iterator().next().getSubjectId()); + assertEquals(LimitViolationType.CURRENT, postContingencyStatus.getPostContingencyViolationsWithoutUncertainties().iterator().next().getLimitType()); + assertEquals(1, postContingencyStatus.getPostContingencyViolationsWithUncertainties().size()); + assertEquals("NHV1_NHV2_2", postContingencyStatus.getPostContingencyViolationsWithUncertainties().iterator().next().getSubjectId()); + assertEquals(LimitViolationType.CURRENT, postContingencyStatus.getPostContingencyViolationsWithUncertainties().iterator().next().getLimitType()); + assertEquals(1, postContingencyStatus.getCurativeActionsApplication().size()); + WCAActionApplication actionApplication = postContingencyStatus.getCurativeActionsApplication().iterator().next(); + assertEquals(action.getId(), actionApplication.getActionId()); + assertTrue(actionApplication.getLoadflowResult().loadflowConverged()); + assertTrue(actionApplication.areViolationsRemoved()); + assertTrue(actionApplication.isActionApplied()); + } + + @Test + public void testRunWithLoadflowDivergence() throws Exception { + LoadFlowResult loadFlowResult = new LoadFlowResult() { + @Override + public boolean isOk() { + return false; + } + + @Override + public Map getMetrics() { + return Collections.emptyMap(); + } + + @Override + public String getLogs() { + return null; + } + }; + LoadFlow loadFlow = Mockito.mock(LoadFlow.class); + Mockito.when(loadFlow.run(Matchers.any(LoadFlowParameters.class))) + .thenReturn(loadFlowResult); + Mockito.when(loadFlow.runAsync(Matchers.any(String.class), Matchers.any(LoadFlowParameters.class))) + .thenReturn(CompletableFuture.completedFuture(loadFlowResult)); + Mockito.when(loadFlowFactory.create(network, computationManager, 0)) + .thenReturn(loadFlow); + + WCAConfig config = new WCAConfig(Paths.get("/xpress-home"), 1f, true, false, EnumSet.noneOf(WCARestrictingThresholdLevel.class), + 0f, true, false, WCAPreventiveActionsFilter.LF, WCAPreventiveActionsOptimizer.NONE, false, + WCACurativeActionsOptimizer.NONE, 0f, EnumSet.noneOf(Country.class), true, true, false); + WCAParameters parameters = new WCAParameters(histoInterval, null, null, 1); + WCA wca = new WCAImpl(network, computationManager, histoDbClient, rulesDbClient, uncertaintiesAnalyserFactory, + contingenciesActionsDbClient, loadFlowFactory, config); + WCAResult result = wca.run(parameters); + WCAReport report = wca.getReport(); + + assertEquals(WCAClusterNum.FOUR, result.getClusters().iterator().next().getNum()); + assertTrue(result.getClusters().iterator().next().getOrigin().contains(WCAClusterOrigin.LF_DIVERGENCE.name())); + + assertFalse(report.getBaseStateLoadflowResult().loadflowConverged()); + } + + @Test + public void testRunWithPreventiveActions() throws Exception { + network.getLine("NHV1_NHV2_2").getTerminal1().setP(860f); + network.getLine("NHV1_NHV2_1").getTerminal1().setP(860f); + + Action action1 = Mockito.mock(Action.class); + Mockito.when(action1.getId()).thenReturn("NHV1_NHV2_2_preventive_action"); + Mockito.when(action1.toTask()).thenReturn(new ModificationTask() { + @Override + public void modify(Network network, ComputationManager computationManager) { + network.getLine("NHV1_NHV2_2").getTerminal1().setP(560f); + } + }); + Action action2 = Mockito.mock(Action.class); + Mockito.when(action2.getId()).thenReturn("NHV1_NHV2_1_preventive_action"); + Mockito.when(action2.toTask()).thenReturn(new ModificationTask() { + @Override + public void modify(Network network, ComputationManager computationManager) { + network.getLine("NHV1_NHV2_1").getTerminal1().setP(850f); + } + }); + Constraint constraint1 = new ConstraintImpl("NHV1_NHV2_2", 0, ConstraintType.BRANCH_OVERLOAD); + Constraint constraint2 = new ConstraintImpl("NHV1_NHV2_1", 0, ConstraintType.BRANCH_OVERLOAD); + ActionsContingenciesAssociation association1 = new ActionsContingenciesAssociationImpl( + Collections.emptyList(), + Arrays.asList(constraint1), + Arrays.asList(action1.getId())); + ActionsContingenciesAssociation association2 = new ActionsContingenciesAssociationImpl( + Collections.emptyList(), + Arrays.asList(constraint2), + Arrays.asList(action2.getId())); + Mockito.when(contingenciesActionsDbClient.getAction(action1.getId(), network)).thenReturn(action1); + Mockito.when(contingenciesActionsDbClient.getActionsCtgAssociationsByConstraint(constraint1.getEquipment(), constraint1.getType())) + .thenReturn(Arrays.asList(association1)); + Mockito.when(contingenciesActionsDbClient.getAction(action2.getId(), network)).thenReturn(action2); + Mockito.when(contingenciesActionsDbClient.getActionsCtgAssociationsByConstraint(constraint2.getEquipment(), constraint2.getType())) + .thenReturn(Arrays.asList(association2)); + + WCADomainsResult domainsResult = new WCADomainsResult(true, false, 1, Collections.emptyMap()); + Mockito.when(computationManager.execute(Mockito.any(ExecutionEnvironment.class), Mockito.any())) + .thenReturn(CompletableFuture.completedFuture(domainsResult)); + + WCAConfig config = new WCAConfig(Paths.get("/xpress-home"), 1f, true, false, EnumSet.noneOf(WCARestrictingThresholdLevel.class), + 0f, true, false, WCAPreventiveActionsFilter.DOMAINS, WCAPreventiveActionsOptimizer.DOMAINS, true, + WCACurativeActionsOptimizer.NONE, 0f, EnumSet.noneOf(Country.class), true, true, true); + WCAParameters parameters = new WCAParameters(histoInterval, null, null, 1); + WCA wca = new WCAImpl(network, computationManager, histoDbClient, rulesDbClient, uncertaintiesAnalyserFactory, + contingenciesActionsDbClient, loadFlowFactory, config); + WCAResult result = wca.run(parameters); + WCAReport report = wca.getReport(); + + assertTrue(result.getClusters().iterator().next().getOrigin().contains(WCAClusterOrigin.LF_BASIC_VIOLATION.name())); + assertTrue(result.getClusters().iterator().next().getOrigin().contains(WCAClusterOrigin.DOMAINS_BASIC_VIOLATION.name())); + assertTrue(result.getClusters().iterator().next().getOrigin().contains(WCAClusterOrigin.DOMAINS_SPECIFIC_PREVENTIVE_ACTION_FOUND.name())); + + assertTrue(report.getBaseStateLoadflowResult().loadflowConverged()); + assertEquals(2, report.getPreContingencyViolationsWithoutUncertainties().size()); + report.getPreContingencyViolationsWithoutUncertainties().forEach( violation -> + { + assertTrue(Arrays.asList("NHV1_NHV2_2","NHV1_NHV2_1").contains(violation.getSubjectId())); + assertEquals(LimitViolationType.CURRENT, violation.getLimitType()); + }); + assertEquals(2, report.getPreContingencyViolationsWithUncertainties().size()); + report.getPreContingencyViolationsWithUncertainties().forEach( violation -> + { + assertTrue(Arrays.asList("NHV1_NHV2_2","NHV1_NHV2_1").contains(violation.getSubjectId())); + assertEquals(LimitViolationType.CURRENT, violation.getLimitType()); + }); + assertEquals(2, report.getPreventiveActionsApplication().size()); + report.getPreventiveActionsApplication().forEach( actionApplication -> + { + assertTrue(Arrays.asList(action1.getId(),action2.getId()).contains(actionApplication.getActionId())); + assertEquals(LimitViolationType.CURRENT, actionApplication.getViolation().getLimitType()); + if ( action1.getId().equals(actionApplication.getActionId()) ) { + assertEquals("NHV1_NHV2_2", actionApplication.getViolation().getSubjectId()); + assertTrue(actionApplication.areViolationsRemoved()); + assertTrue(actionApplication.isActionApplied()); + } + if ( action2.getId().equals(actionApplication.getActionId()) ) { + assertEquals("NHV1_NHV2_1", actionApplication.getViolation().getSubjectId()); + assertFalse(actionApplication.areViolationsRemoved()); + assertFalse(actionApplication.isActionApplied()); + } + }); + assertEquals(1, report.getPostPreventiveActionsViolationsWithUncertainties().size()); + assertEquals("NHV1_NHV2_1", report.getPostPreventiveActionsViolationsWithUncertainties().iterator().next().getSubjectId()); + assertEquals(LimitViolationType.CURRENT, report.getPostPreventiveActionsViolationsWithUncertainties().iterator().next().getLimitType()); + } + + @Test + public void testRunWithPostcontingencyLoadflowDivergence() throws Exception { + LoadFlowResult loadFlowResult = new LoadFlowResult() { + @Override + public boolean isOk() { + if ( StateManager.INITIAL_STATE_ID.equals(network.getStateManager().getWorkingStateId()) ) + return true; + return false; + } + + @Override + public Map getMetrics() { + return Collections.emptyMap(); + } + + @Override + public String getLogs() { + return null; + } + }; + LoadFlow loadFlow = Mockito.mock(LoadFlow.class); + Mockito.when(loadFlow.run(Matchers.any(LoadFlowParameters.class))) + .thenReturn(loadFlowResult); + Mockito.when(loadFlow.runAsync(Matchers.any(String.class), Matchers.any(LoadFlowParameters.class))) + .thenReturn(CompletableFuture.completedFuture(loadFlowResult)); + Mockito.when(loadFlowFactory.create(network, computationManager, 0)) + .thenReturn(loadFlow); + + WCAConfig config = new WCAConfig(Paths.get("/xpress-home"), 1f, true, false, EnumSet.noneOf(WCARestrictingThresholdLevel.class), + 0f, true, false, WCAPreventiveActionsFilter.LF, WCAPreventiveActionsOptimizer.NONE, false, + WCACurativeActionsOptimizer.NONE, 0f, EnumSet.noneOf(Country.class), true, true, false); + WCAParameters parameters = new WCAParameters(histoInterval, null, null, 1); + WCA wca = new WCAImpl(network, computationManager, histoDbClient, rulesDbClient, uncertaintiesAnalyserFactory, + contingenciesActionsDbClient, loadFlowFactory, config); + WCAResult result = wca.run(parameters); + WCAReport report = wca.getReport(); + + assertEquals(WCAClusterNum.FOUR, result.getClusters().iterator().next().getNum()); + assertTrue(result.getClusters().iterator().next().getOrigin().contains(WCAClusterOrigin.LF_POST_CONTINGENCY_DIVERGENCE.name())); + + assertTrue(report.getBaseStateLoadflowResult().loadflowConverged()); + assertTrue(report.getPreContingencyViolationsWithoutUncertainties().isEmpty()); + assertTrue(report.getPreContingencyViolationsWithUncertainties().isEmpty()); + assertFalse(report.getPostContingenciesStatus().iterator().next().getPostContingencyLoadflowResult().loadflowConverged()); + } + + @Test + public void testRunWithSecurityRules() throws Exception { + String workflowId = "workflowId"; + RuleId ruleId = new RuleId(RuleAttributeSet.WORST_CASE, new SecurityIndexId(contingency.getId(), SecurityIndexType.TSO_OVERLOAD)); + ExpressionNode condition = new AndOperator( + new ComparisonOperator( + new Attribute(new HistoDbNetworkAttributeId("LOAD", HistoDbAttr.P)), + new Litteral(500d), + ComparisonOperator.Type.GREATER_EQUAL), + new ComparisonOperator( + new Attribute(new HistoDbNetworkAttributeId("GEN", HistoDbAttr.Q)), + new Litteral(400d), + ComparisonOperator.Type.LESS)); + SecurityRule rule = Mockito.mock(SecurityRule.class); + Mockito.when(rule.getId()).thenReturn(ruleId); + Mockito.when(rule.getWorkflowId()).thenReturn(workflowId); + Mockito.when(rule.toExpression(Mockito.anyFloat())).thenReturn(new SecurityRuleExpression(ruleId, SecurityRuleStatus.SECURE_IF, condition)); + Mockito.when(rulesDbClient.getRules(workflowId, RuleAttributeSet.WORST_CASE, contingency.getId(), SecurityIndexType.TSO_OVERLOAD)) + .thenReturn(Collections.singletonList(rule)); + Mockito.when(rulesDbClient.getRules(workflowId, RuleAttributeSet.MONTE_CARLO, contingency.getId(), SecurityIndexType.TSO_OVERLOAD)) + .thenReturn(Collections.emptyList()); + + WCADomainsResult domainsResult = new WCADomainsResult(false, true, 0, Collections.emptyMap()); + Mockito.when(computationManager.execute(Mockito.any(ExecutionEnvironment.class), Mockito.any())) + .thenReturn(CompletableFuture.completedFuture(domainsResult)); + + WCAConfig config = new WCAConfig(Paths.get("/xpress-home"), 1f, true, false, EnumSet.noneOf(WCARestrictingThresholdLevel.class), + 0f, true, false, WCAPreventiveActionsFilter.LF, WCAPreventiveActionsOptimizer.NONE, false, + WCACurativeActionsOptimizer.NONE, 0f, EnumSet.noneOf(Country.class), true, true, false); + WCAParameters parameters = new WCAParameters(histoInterval, workflowId, Collections.singleton(SecurityIndexType.TSO_OVERLOAD), 1); + WCA wca = new WCAImpl(network, computationManager, histoDbClient, rulesDbClient, uncertaintiesAnalyserFactory, + contingenciesActionsDbClient, loadFlowFactory, config); + WCAResult result = wca.run(parameters); + WCAReport report = wca.getReport(); + + assertTrue(result.getClusters().iterator().next().getOrigin().contains(WCAClusterOrigin.LF_RULE_VIOLATION.name())); + assertTrue(result.getClusters().iterator().next().getOrigin().contains(WCAClusterOrigin.DOMAINS_RULE_VIOLATION.name())); + + assertEquals(2, report.getSecurityRulesApplication().size()); + report.getSecurityRulesApplication().forEach( ruleApplication -> + { + assertEquals(contingency.getId(), ruleApplication.getContingencyId()); + if ( ruleApplication.getSecurityRule() != null ) { + assertEquals(ruleId, ruleApplication.getSecurityRule().getId()); + assertEquals(workflowId, ruleApplication.getSecurityRule().getWorkflowId()); + assertFalse(ruleApplication.isRuleViolated()); + assertEquals(WCARuleViolationType.NO_VIOLATION, ruleApplication.getRuleViolationType()); + } else { + assertTrue(ruleApplication.isRuleViolated()); + assertEquals(WCARuleViolationType.MISSING_RULE, ruleApplication.getRuleViolationType()); + } + }); + } + +} + diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/WCAReportImplTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/WCAReportImplTest.java new file mode 100644 index 00000000..d2b459e4 --- /dev/null +++ b/wca-integration/src/test/java/eu/itesla_project/wca/WCAReportImplTest.java @@ -0,0 +1,385 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.Locale; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import com.google.common.io.CharStreams; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; + +import eu.itesla_project.iidm.network.Country; +import eu.itesla_project.modules.rules.RuleAttributeSet; +import eu.itesla_project.modules.rules.RuleId; +import eu.itesla_project.modules.rules.SecurityRule; +import eu.itesla_project.modules.wca.report.WCAActionApplication; +import eu.itesla_project.modules.wca.report.WCALoadflowResult; +import eu.itesla_project.modules.wca.report.WCAPostContingencyStatus; +import eu.itesla_project.modules.wca.report.WCARuleViolationType; +import eu.itesla_project.modules.wca.report.WCASecurityRuleApplication; +import eu.itesla_project.security.LimitViolation; +import eu.itesla_project.security.LimitViolationType; +import eu.itesla_project.simulation.securityindexes.SecurityIndexId; +import eu.itesla_project.simulation.securityindexes.SecurityIndexType; +import eu.itesla_project.wca.report.WCAReportImpl; + +/** + * + * @author Massimo Ferraro + */ +public class WCAReportImplTest { + + private FileSystem fileSystem; + private LimitViolation line1Violation; + private LimitViolation line2Violation; + + @Before + public void setUp() throws Exception { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + + line1Violation = new LimitViolation("line1", LimitViolationType.CURRENT, 1000f, "20'", 1, 1100f, Country.FR, 380f); + line2Violation = new LimitViolation("line2", LimitViolationType.CURRENT, 900f, "10'", 1, 950f, Country.FR, 380f); + } + + @After + public void tearDown() throws Exception { + fileSystem.close(); + } + + @Test + public void testExportPreContigencyViolationsWithoutUncertaintiesLoadflowDivergence() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + wcaReport.setBaseStateLoadflowResult(new WCALoadflowResult(false, "base state loadflow diverged")); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, + "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", + "network1;Loadflow;base state loadflow diverged;;;;;;"); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportPreContigencyViolationsWithoutUncertainties() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + wcaReport.setBaseStateLoadflowResult(new WCALoadflowResult(true, null)); + wcaReport.setPreContingencyViolationsWithoutUncertainties(Arrays.asList(line1Violation, line2Violation)); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, + "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", + "network1;;;CURRENT;line1;" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f), + "network1;;;CURRENT;line2;" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f)); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportPreContigencyViolationsWithUncertaintiesLoadflowDivergence() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + wcaReport.setBaseStateWithUncertaintiesLoadflowResult(new WCALoadflowResult(false, "base state with uncertainties loadflow diverged")); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, + "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", + "network1;Loadflow;base state with uncertainties loadflow diverged;;;;;;"); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportPreContigencyViolationsWithUncertainties() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + wcaReport.setBaseStateLoadflowResult(new WCALoadflowResult(true, null)); + wcaReport.setPreContingencyViolationsWithUncertainties(Arrays.asList(line1Violation, line2Violation)); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, + "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", + "network1;;;CURRENT;line1;" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f), + "network1;;;CURRENT;line2;" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f)); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportPreventiveActionsApplication() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + wcaReport.addPreventiveActionApplication(new WCAActionApplication("action1", + line1Violation, + new WCALoadflowResult(true, null), + false, + false, + "post action state contains new violations")); + wcaReport.addPreventiveActionApplication(new WCAActionApplication("action2", + line1Violation, + new WCALoadflowResult(true, null), + true, + true, + null)); + wcaReport.addPreventiveActionApplication(new WCAActionApplication("action3", + line2Violation, + new WCALoadflowResult(false, "loadflow on post action state diverged"), + false, + false, + null)); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.POST_PREVENTIVE_ACTIONS_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.POST_PREVENTIVE_ACTIONS_TITLE, + "Basecase;ActionId;ViolatedEquipment;ViolationType;FailureStep;FailureDescription;ViolationRemoved;ActionApplied;Comment", + "network1;action1;line1;CURRENT;;;false;false;post action state contains new violations", + "network1;action2;line1;CURRENT;;;true;true;", + "network1;action3;line2;CURRENT;Loadflow;loadflow on post action state diverged;false;false;"); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportPostPreventiveActionsViolationsWithUncertainties() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + wcaReport.setBaseStateLoadflowResult(new WCALoadflowResult(true, null)); + wcaReport.setPostPreventiveActionsViolationsWithUncertainties(Arrays.asList(line1Violation, line2Violation)); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.POST_PREVENTIVE_ACTIONS_VIOLATIONS_WITH_UNCERTAINTIES_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.POST_PREVENTIVE_ACTIONS_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, + "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", + "network1;;;CURRENT;line1;" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f), + "network1;;;CURRENT;line2;" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f)); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportSecurityRulesApplication() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + SecurityRule rule1 = Mockito.mock(SecurityRule.class); + Mockito.when(rule1.getId()).thenReturn(new RuleId(RuleAttributeSet.WORST_CASE, new SecurityIndexId("fault1", SecurityIndexType.TSO_OVERLOAD))); + Mockito.when(rule1.getWorkflowId()).thenReturn("workflow-0"); + SecurityRule rule2 = Mockito.mock(SecurityRule.class); + Mockito.when(rule2.getId()).thenReturn(new RuleId(RuleAttributeSet.WORST_CASE, new SecurityIndexId("fault1", SecurityIndexType.SMALLSIGNAL))); + Mockito.when(rule2.getWorkflowId()).thenReturn("workflow-0"); + wcaReport.addSecurityRulesApplication(new WCASecurityRuleApplication("fault1", + rule1, + true, + WCARuleViolationType.MISSING_ATTRIBUTE, + "Missing attributes for rule " + rule1.getId()+ ": attribute1")); + wcaReport.addSecurityRulesApplication(new WCASecurityRuleApplication("fault1", + rule2, + false, + WCARuleViolationType.NO_VIOLATION, + "Rule " + rule2.getId() + " verified")); + String missingRuleMessage = "Missing rule " + new RuleId(RuleAttributeSet.WORST_CASE, new SecurityIndexId("fault2", SecurityIndexType.TSO_OVERLOAD)); + wcaReport.addSecurityRulesApplication(new WCASecurityRuleApplication("fault2", + null, + true, + WCARuleViolationType.MISSING_RULE, + missingRuleMessage)); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.SECURITY_RULES_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.SECURITY_RULES_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, + "Basecase;ContingencyId;SecurityRule;WorkflowId;RuleViolated;ViolationType;Cause", + "network1;fault1;"+rule1.getId().toString()+";workflow-0;true;MISSING_ATTRIBUTE;Missing attributes for rule " + rule1.getId()+ ": attribute1", + "network1;fault1;"+rule2.getId().toString()+";workflow-0;false;NO_VIOLATION;Rule " + rule2.getId() + " verified", + "network1;fault2;;;true;MISSING_RULE;"+ missingRuleMessage); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportPostContigencyViolationsWithoutUncertaintiesLoadflowDivergence() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + WCAPostContingencyStatus postContingencyStatus = new WCAPostContingencyStatus("fault1", new WCALoadflowResult(false, "post contingency loadflow diverged")); + wcaReport.addPostContingencyStatus(postContingencyStatus); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, + "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", + "network1;fault1;Loadflow;post contingency loadflow diverged;;;;;;"); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportPostContigencyViolationsWithoutUncertainties() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + WCAPostContingencyStatus postContingencyStatus1 = new WCAPostContingencyStatus("fault1", new WCALoadflowResult(true, null)); + postContingencyStatus1.setPostContingencyViolationsWithoutUncertainties(Collections.singletonList(line1Violation)); + wcaReport.addPostContingencyStatus(postContingencyStatus1); + WCAPostContingencyStatus postContingencyStatus2 = new WCAPostContingencyStatus("fault2", new WCALoadflowResult(true, null)); + postContingencyStatus2.setPostContingencyViolationsWithoutUncertainties(Collections.singletonList(line2Violation)); + wcaReport.addPostContingencyStatus(postContingencyStatus2); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, + "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", + "network1;fault1;;;CURRENT;line1;" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f), + "network1;fault2;;;CURRENT;line2;" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f)); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportPostContigencyViolationsWithUncertaintiesLoadflowDivergence() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + WCAPostContingencyStatus postContingencyStatus = new WCAPostContingencyStatus("fault1", new WCALoadflowResult(true, null)); + postContingencyStatus.setPostContingencyWithUncertaintiesLoadflowResult(new WCALoadflowResult(false, "post contingency with uncertainties loadflow diverged")); + wcaReport.addPostContingencyStatus(postContingencyStatus); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, + "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", + "network1;fault1;Loadflow;post contingency with uncertainties loadflow diverged;;;;;;"); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportPostContigencyViolationsWithUncertainties() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + WCAPostContingencyStatus postContingencyStatus1 = new WCAPostContingencyStatus("fault1", new WCALoadflowResult(true, null)); + postContingencyStatus1.setPostContingencyWithUncertaintiesLoadflowResult(new WCALoadflowResult(true, null)); + postContingencyStatus1.setPostContingencyViolationsWithUncertainties(Collections.singletonList(line1Violation)); + wcaReport.addPostContingencyStatus(postContingencyStatus1); + WCAPostContingencyStatus postContingencyStatus2 = new WCAPostContingencyStatus("fault2", new WCALoadflowResult(true, null)); + postContingencyStatus2.setPostContingencyWithUncertaintiesLoadflowResult(new WCALoadflowResult(true, null)); + postContingencyStatus2.setPostContingencyViolationsWithUncertainties(Collections.singletonList(line2Violation)); + wcaReport.addPostContingencyStatus(postContingencyStatus2); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, + "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", + "network1;fault1;;;CURRENT;line1;" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f), + "network1;fault2;;;CURRENT;line2;" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f)); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportCurativeActionsApplication() throws IOException { + Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); + + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + WCAPostContingencyStatus postContingencyStatus1 = new WCAPostContingencyStatus("fault1", new WCALoadflowResult(true, null)); + postContingencyStatus1.setCurativeActionsApplication(Arrays.asList(new WCAActionApplication("action1", + null, + new WCALoadflowResult(true, null), + false, + false, + "violantions found in post action state"), + new WCAActionApplication("action2", + null, + new WCALoadflowResult(true, null), + true, + true, + null))); + wcaReport.addPostContingencyStatus(postContingencyStatus1); + WCAPostContingencyStatus postContingencyStatus2 = new WCAPostContingencyStatus("fault2", new WCALoadflowResult(true, null)); + postContingencyStatus2.setCurativeActionsApplication(Arrays.asList(new WCAActionApplication("action3", + null, + new WCALoadflowResult(false, "loadflow on post action state diverged"), + false, + false, + null))); + wcaReport.addPostContingencyStatus(postContingencyStatus2); + wcaReport.exportCsv(folder); + + Path report = folder.resolve(WCAReportImpl.POST_CURATIVE_ACTIONS_FILE); + assertTrue(Files.exists(report)); + String reportContent = String.join(System.lineSeparator(), + WCAReportImpl.POST_CURATIVE_ACTIONS_TITLE, + "Basecase;Contingency;ActionId;ViolatedEquipment;ViolationType;FailureStep;FailureDescription;ViolationRemoved;ActionApplied;Comment", + "network1;fault1;action1;;;;;false;false;violantions found in post action state", + "network1;fault1;action2;;;;;true;true;", + "network1;fault2;action3;;;Loadflow;loadflow on post action state diverged;false;false;"); + assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); + } + + @Test + public void testExportCreateFolder() throws IOException { + Path folder = fileSystem.getPath("/export-folder"); + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + wcaReport.exportCsv(folder); + assertTrue(Files.exists(folder)); + } + + @Test + public void testFailExportToFile() throws IOException { + Path file = Files.createFile(fileSystem.getPath("/file")); + WCAReportImpl wcaReport = new WCAReportImpl("network1"); + assertFalse(wcaReport.exportCsv(file)); + } +} diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/WCASecurityRulesWriterTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/WCASecurityRulesWriterTest.java new file mode 100644 index 00000000..66700e65 --- /dev/null +++ b/wca-integration/src/test/java/eu/itesla_project/wca/WCASecurityRulesWriterTest.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2017, 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 eu.itesla_project.wca; + +import static org.junit.Assert.assertEquals; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import eu.itesla_project.commons.util.StringToIntMapper; +import eu.itesla_project.contingency.BranchContingency; +import eu.itesla_project.contingency.Contingency; +import eu.itesla_project.contingency.ContingencyElement; +import eu.itesla_project.contingency.ContingencyImpl; +import eu.itesla_project.iidm.datasource.MemDataSource; +import eu.itesla_project.iidm.export.ampl.AmplSubset; +import eu.itesla_project.iidm.export.ampl.AmplUtil; +import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.iidm.network.test.NetworkTest1Factory; +import eu.itesla_project.modules.histo.HistoDbAttr; +import eu.itesla_project.modules.histo.HistoDbNetworkAttributeId; +import eu.itesla_project.modules.rules.RuleAttributeSet; +import eu.itesla_project.modules.rules.RuleId; +import eu.itesla_project.modules.rules.SecurityRuleExpression; +import eu.itesla_project.modules.rules.SecurityRuleStatus; +import eu.itesla_project.modules.rules.expr.AndOperator; +import eu.itesla_project.modules.rules.expr.Attribute; +import eu.itesla_project.modules.rules.expr.ComparisonOperator; +import eu.itesla_project.modules.rules.expr.ExpressionNode; +import eu.itesla_project.modules.rules.expr.Litteral; +import eu.itesla_project.simulation.securityindexes.SecurityIndexId; +import eu.itesla_project.simulation.securityindexes.SecurityIndexType; + +/** + * + * @author Massimo Ferraro + */ +public class WCASecurityRulesWriterTest { + + @Test + public void testWrite() { + ContingencyElement contingencyElement = new BranchContingency("line"); + Contingency contingency = new ContingencyImpl("contigency_1", contingencyElement); + + Network network = NetworkTest1Factory.create(); + + List rules = new ArrayList<>(); + SecurityIndexType securityIndexType = SecurityIndexType.TSO_OVERLOAD; + SecurityIndexId securityIndexId = new SecurityIndexId(contingency.getId(), securityIndexType); + RuleAttributeSet ruleAttributeSet = RuleAttributeSet.WORST_CASE; + RuleId ruleId = new RuleId(ruleAttributeSet, securityIndexId); + ComparisonOperator condition1 = new ComparisonOperator( + new Attribute(new HistoDbNetworkAttributeId(network.getLoads().iterator().next().getId(), HistoDbAttr.P)), + new Litteral(10d), + ComparisonOperator.Type.GREATER_EQUAL); + ComparisonOperator condition2 = new ComparisonOperator( + new Attribute(new HistoDbNetworkAttributeId(network.getGenerators().iterator().next().getId(), HistoDbAttr.Q)), + new Litteral(5d), + ComparisonOperator.Type.LESS); + ExpressionNode condition = new AndOperator(condition1, condition2); + rules.add(new SecurityRuleExpression(ruleId, SecurityRuleStatus.SECURE_IF, condition)); + + MemDataSource dataSource = new MemDataSource(); + + StringToIntMapper mapper = new StringToIntMapper<>(AmplSubset.class); + AmplUtil.fillMapper(mapper, network); + mapper.newInt(AmplSubset.FAULT, contingency.getId()); + + new WCASecurityRulesWriter(network, rules, dataSource, mapper, false, false).write(); + + String fileContent = String.join(System.lineSeparator(), + "#(("+ network.getLoads().iterator().next().getId() + "_P >= 10.0) AND ("+ network.getGenerators().iterator().next().getId() + "_Q < 5.0))", + "#Security rules", + "#\"inequality num\" \"convex num\" \"var type (1: P, 2: Q, 3: V)\" \"entity type (1: branch, 2: load, 3: generator, 4: compensator shunt, 5: substation)\" \"entity num\" \"branch side (1 or 2, 0 if NA)\" \"inequality coeff.\" \"constant value\" \"contingency num\" \"security index type\" \"attribute set (0: active only, 1: active/reactive)\"", + "1 1 1 2 1 0 -1.00000 -10.0000 1 4 0", + "2 1 2 3 1 0 1.00000 5.00000 1 4 0"); + assertEquals(fileContent, new String(dataSource.getData(WCAConstants.SECURITY_RULES_FILE_SUFFIX, WCAConstants.TXT_EXT), StandardCharsets.UTF_8).trim()); + } + +} diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/WCAUtilsTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/WCAUtilsTest.java index 10b51fdd..4b119bef 100644 --- a/wca-integration/src/test/java/eu/itesla_project/wca/WCAUtilsTest.java +++ b/wca-integration/src/test/java/eu/itesla_project/wca/WCAUtilsTest.java @@ -6,13 +6,21 @@ */ package eu.itesla_project.wca; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.zip.GZIPInputStream; import org.apache.commons.io.IOUtils; @@ -25,8 +33,19 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; +import eu.itesla_project.commons.util.StringToIntMapper; +import eu.itesla_project.contingency.BranchContingency; +import eu.itesla_project.contingency.Contingency; +import eu.itesla_project.contingency.ContingencyElement; +import eu.itesla_project.contingency.ContingencyImpl; +import eu.itesla_project.iidm.datasource.MemDataSource; +import eu.itesla_project.iidm.export.ampl.AmplSubset; import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.iidm.network.StateManager; import eu.itesla_project.iidm.network.test.NetworkTest1Factory; +import eu.itesla_project.modules.wca.WCAClusterNum; +import eu.itesla_project.security.LimitViolation; +import eu.itesla_project.security.LimitViolationType; /** * @@ -59,4 +78,119 @@ public void testExportState() throws IOException, URISyntaxException { assertTrue(IOUtils.contentEquals(getClass().getResourceAsStream("/network1.xiidm"), stream)); } } + + private void testInjection(Map injections, String injection, float value) { + assertTrue(injections.containsKey(injection)); + assertEquals(value, injections.get(injection), 0); + } + + @Test + public void testReadDomainsResult() throws URISyntaxException, IOException { + Path workingDir = Paths.get(getClass().getResource("/domains").toURI()); + + WCADomainsResult domainsResult = WCAUtils.readDomainsResult("wca_domains_1", workingDir, "wca_uncertainties.txt"); + assertFalse(domainsResult.foundBasicViolations()); + assertFalse(domainsResult.areRulesViolated()); + assertEquals(0, domainsResult.getPreventiveActionIndex()); + testInjection(domainsResult.getInjections(), "LOAD_1", -1.1f); + testInjection(domainsResult.getInjections(), "LOAD_2", 1.5f); + testInjection(domainsResult.getInjections(), "GEN_1", 1.2f); + + domainsResult = WCAUtils.readDomainsResult("wca_domains_2", workingDir, "wca_uncertainties.txt"); + assertTrue(domainsResult.foundBasicViolations()); + assertFalse(domainsResult.areRulesViolated()); + assertEquals(0, domainsResult.getPreventiveActionIndex()); + testInjection(domainsResult.getInjections(), "LOAD_1", -1.1f); + testInjection(domainsResult.getInjections(), "LOAD_2", 1.5f); + testInjection(domainsResult.getInjections(), "GEN_1", 1.2f); + } + + @Test + public void testReadClustersResults() throws URISyntaxException, IOException { + Path workingDir = Paths.get(getClass().getResource("/clusters").toURI()); + + WCAClustersResult clustersResult = WCAUtils.readClustersResult("wca_clusters_1", workingDir, "wca_flow_1.txt", "wca_uncertainties.txt"); + assertEquals(WCAClusterNum.TWO, clustersResult.getClusterNum()); + assertFalse(clustersResult.foundViolations()); + assertEquals(1, clustersResult.getCurativeActionIndex()); + testInjection(clustersResult.getInjections(), "LOAD_1", -1.1f); + testInjection(clustersResult.getInjections(), "LOAD_2", 1.5f); + testInjection(clustersResult.getInjections(), "GEN_1", 1.2f); + + clustersResult = WCAUtils.readClustersResult("wca_clusters_2", workingDir, "wca_flow_2.txt", "wca_uncertainties.txt"); + assertEquals(WCAClusterNum.ONE, clustersResult.getClusterNum()); + assertTrue(clustersResult.foundViolations()); + assertEquals(0, clustersResult.getCurativeActionIndex()); + testInjection(clustersResult.getInjections(), "LOAD_1", -1.1f); + testInjection(clustersResult.getInjections(), "LOAD_2", 1.5f); + testInjection(clustersResult.getInjections(), "GEN_1", 1.2f); + } + + private void assertEqualsToRef(MemDataSource dataSource, String fileSuffix, String title, String id) { + String fileContent = "#" + title + System.lineSeparator() + + "#\"num\" \"id\"" + System.lineSeparator() + + "1 \"" + id + "\"" + System.lineSeparator(); + assertEquals(fileContent, new String(dataSource.getData(fileSuffix, WCAConstants.TXT_EXT), StandardCharsets.UTF_8)); + } + + @Test + public void testWriteContingencies() { + ContingencyElement contingencyElement = new BranchContingency("line"); + Contingency contingency = new ContingencyImpl("contigency_1", contingencyElement); + MemDataSource dataSource = new MemDataSource(); + StringToIntMapper mapper = new StringToIntMapper<>(AmplSubset.class); + + mapper.newInt(AmplSubset.FAULT, contingency.getId()); + WCAUtils.writeContingencies(Collections.singleton(contingency), dataSource, mapper); + assertEqualsToRef(dataSource, WCAConstants.FAULTS_FILE_SUFFIX, "Contingencies", "contigency_1"); + } + + @Test + public void testWriteActions() { + MemDataSource dataSource = new MemDataSource(); + StringToIntMapper mapper = new StringToIntMapper<>(AmplSubset.class); + + mapper.newInt(AmplSubset.CURATIVE_ACTION, "action_1"); + WCAUtils.writeActions(Collections.singleton("action_1"), dataSource, mapper, "Curative actions", AmplSubset.CURATIVE_ACTION); + assertEqualsToRef(dataSource, WCAConstants.ACTIONS_FILE_SUFFIX, "Curative actions", "action_1"); + + mapper.newInt(AmplSubset.PREVENTIVE_ACTION, "action_1"); + WCAUtils.writeActions(Collections.singleton("action_1"), dataSource, mapper, "Preventive actions", AmplSubset.PREVENTIVE_ACTION); + assertEqualsToRef(dataSource, WCAConstants.ACTIONS_FILE_SUFFIX, "Preventive actions", "action_1"); + } + + @Test + public void testApplyInjections() { + Network network = NetworkTest1Factory.create(); + // fix missing terminal values + network.getGenerator("generator1").getTerminal().setP(-network.getGenerator("generator1").getTargetP()); + network.getLoad("load1").getTerminal().setP(network.getLoad("load1").getP0()); + + float loadP = network.getLoad("load1").getTerminal().getP(); + float loadP0 = network.getLoad("load1").getP0(); + float generatorP = network.getGenerator("generator1").getTerminal().getP(); + float generatorTargetP = network.getGenerator("generator1").getTargetP(); + + Map injections = new HashMap(); + injections.put("load1", 10f); + injections.put("generator1", -10f); + WCAUtils.applyInjections(network, StateManager.INITIAL_STATE_ID, injections); + + assertEquals(loadP+10, network.getLoad("load1").getTerminal().getP(), 0); + assertEquals(loadP0+10, network.getLoad("load1").getP0(), 0); + assertEquals(generatorP-10, network.getGenerator("generator1").getTerminal().getP(), 0); + assertEquals(generatorTargetP+10, network.getGenerator("generator1").getTargetP(), 0); + } + + @Test + public void testContainsViolation() { + LimitViolation line1Violation = new LimitViolation("line1", LimitViolationType.CURRENT, 1000f, "10'", 1100f); + LimitViolation line2Violation = new LimitViolation("line2", LimitViolationType.CURRENT, 900f, "20'", 950f); + LimitViolation line3Violation = new LimitViolation("line3", LimitViolationType.CURRENT, 1000f, "30'", 1300f); + + assertFalse(WCAUtils.containsViolation(Arrays.asList(line1Violation, line3Violation), line2Violation)); + assertTrue(WCAUtils.containsViolation(Arrays.asList(line1Violation, line2Violation, line3Violation), line2Violation)); + + } + } diff --git a/wca-integration/src/test/resources/clusters/wca_clusters_1_0.out b/wca-integration/src/test/resources/clusters/wca_clusters_1_0.out new file mode 100644 index 00000000..372f92c8 --- /dev/null +++ b/wca-integration/src/test/resources/clusters/wca_clusters_1_0.out @@ -0,0 +1,23 @@ + WCA Result : contingency index 1 curative 0 line or transformer "LINE_1" flow 2017.46 A threshold 1800 A sending end + WCA Result : contingency index 1 curative 0 line or transformer "LINE_1" flow 2017.46 A threshold 1800 A receiving end + WCA Result : contingency index 1 contingency name "CONTINGENCY_1" line or transformer deleted "LINE_2" + WCA Result : contingency index 1 contingency name "CONTINGENCY_1" line or transformer deleted "LINE_3" + WCA Result : contingency index 1 no security rules + WCA Result : contingency index 1 curative 0 BILEVEL contingency name "CONTINGENCY_1" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties 1377.68 MW without 1374.9 MW limit 1217.8 MW line or transformer "LINE_1" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties 176.878 MW without 177.982 MW limit 423.985 MW PST "LINE_4" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties 338.112 MW without 338.422 MW limit 1000.01 MW PST "LINE_5" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties 47.6029 MW without 48.125 MW limit 380.893 MW PST "LINE_6" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties 250.246 MW without 248.895 MW limit 41193.6 MW PST "LINE_7" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties -1377.68 MW without -1374.9 MW limit 1217.8 MW PST "LINE_1" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties 52.3156 MW without 51.5405 MW limit 430.983 MW PST "LINE_8" + WCA Result : contingency index 1 curative 1 BILEVEL curative name "ACTION_1" + WCA Result : contingency index 1 curative 1 BILEVEL contingency name "CONTINGENCY_1" + WCA Result : contingency index 1 curative 1 BILEVEL flow with uncertainties 1167.94 MW without 1165.19 MW limit 1235.1 MW line or transformer "LINE_1 + WCA Result : contingency index 1 curative 1 BILEVEL flow with uncertainties 176.801 MW without 177.905 MW limit 423.981 MW PST "LINE_4" + WCA Result : contingency index 1 curative 1 BILEVEL flow with uncertainties 339.532 MW without 339.84 MW limit 999.526 MW PST "LINE_5" + WCA Result : contingency index 1 curative 1 BILEVEL flow with uncertainties 47.7227 MW without 48.2442 MW limit 380.89 MW PST "LINE_6" + WCA Result : contingency index 1 curative 1 BILEVEL flow with uncertainties 247.674 MW without 246.325 MW limit 41196 MW PST "LINE_7" + WCA Result : contingency index 1 curative 1 BILEVEL flow with uncertainties -1167.94 MW without -1165.19 MW limit 1235.1 MW PST "LINE_1" + WCA Result : contingency index 1 curative 1 BILEVEL flow with uncertainties 52.1049 MW without 51.3264 MW limit 430.987 MW PST "LINE_8" + WCA Result : contingency_index 1 contingency_cluster_index 2 curative_action_index 1 diff --git a/wca-integration/src/test/resources/clusters/wca_clusters_2_0.out b/wca-integration/src/test/resources/clusters/wca_clusters_2_0.out new file mode 100644 index 00000000..8a16c969 --- /dev/null +++ b/wca-integration/src/test/resources/clusters/wca_clusters_2_0.out @@ -0,0 +1,11 @@ + WCA Result : contingency index 1 contingency name "CONTINGENCY_1" line or transformer deleted "LINE_1" + WCA Result : contingency index 1 contingency name "CONTINGENCY_1" line or transformer deleted "LINE_2" + WCA Result : contingency index 1 no security rules + WCA Result : contingency index 1 curative 0 BILEVEL contingency name "CONTINGENCY_1" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties 344.338 MW without 340.317 MW limit 393.245 MW line or transformer "LINE_3" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties 199.696 MW without 199.604 MW limit 425.85 MW PST "LINE_4" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties 517.658 MW without 514.512 MW limit 997.169 MW PST "LINE_5" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties 286.464 MW without 289.696 MW limit 41160.9 MW PST "LINE_6" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties -513.326 MW without -515.407 MW limit 1249.4 MW PST "LINE_7" + WCA Result : contingency index 1 curative 0 BILEVEL flow with uncertainties 29.0083 MW without 31.4338 MW limit 434.166 MW PST "LINE_8" + WCA Result : contingency_index 1 contingency_cluster_index 1 curative_action_index 0 diff --git a/wca-integration/src/test/resources/clusters/wca_flow_1.txt b/wca-integration/src/test/resources/clusters/wca_flow_1.txt new file mode 100644 index 00000000..bc067a0b --- /dev/null +++ b/wca-integration/src/test/resources/clusters/wca_flow_1.txt @@ -0,0 +1,4 @@ +"BRANCH_1";-56.17499837;91.67715445 +"BRANCH_2";-8.96902000;53.80606688 +"BRANCH_3";-637.40210074;1709.16326234 +"BRANCH_4";-238.42254210;4005.24838586 diff --git a/wca-integration/src/test/resources/clusters/wca_flow_2.txt b/wca-integration/src/test/resources/clusters/wca_flow_2.txt new file mode 100644 index 00000000..971d69e0 --- /dev/null +++ b/wca-integration/src/test/resources/clusters/wca_flow_2.txt @@ -0,0 +1,4 @@ +"BRANCH_1";-56.17499837;91.67715445 +"BRANCH_2";-58.96902000;53.80606688 +"BRANCH_3";-637.40210074;1709.16326234 +"BRANCH_4";-238.42254210;4005.24838586 diff --git a/wca-integration/src/test/resources/clusters/wca_uncertainties.txt b/wca-integration/src/test/resources/clusters/wca_uncertainties.txt new file mode 100644 index 00000000..8042c461 --- /dev/null +++ b/wca-integration/src/test/resources/clusters/wca_uncertainties.txt @@ -0,0 +1,3 @@ +"LOAD_1";-1.1 +"LOAD_2";1.5 +"GEN_1";1.2 diff --git a/wca-integration/src/test/resources/domains/wca_domains_1_0.out b/wca-integration/src/test/resources/domains/wca_domains_1_0.out new file mode 100644 index 00000000..763e8ee6 --- /dev/null +++ b/wca-integration/src/test/resources/domains/wca_domains_1_0.out @@ -0,0 +1,5 @@ + WCA Result : contingency index 1 preventive 0 no security rules + WCA Result : base case + WCA Result : preventive action index 0 BILEVEL flow with uncertainties 451.001 MW without 435.232 MW limit 475.407 MW line or transformer "TRANSFORMER_1" + WCA Result : basic limits no violations + WCA Result : basic_violation 0 rule_violation 0 preventive_action_index 0 diff --git a/wca-integration/src/test/resources/domains/wca_domains_2_0.out b/wca-integration/src/test/resources/domains/wca_domains_2_0.out new file mode 100644 index 00000000..037dac0b --- /dev/null +++ b/wca-integration/src/test/resources/domains/wca_domains_2_0.out @@ -0,0 +1,5 @@ + WCA Result : contingency index 1 preventive 0 no security rules + WCA Result : base case + WCA Result : preventive action index 0 BILEVEL flow with uncertainties 73.5138 MW without 52.8462 MW limit 71.04 MW line or transformer "TRANSFORMER_2" + WCA Result : basic limits most severe violation on line or transformer "TRANSFORMER_2" flow with uncertainties 73.5138 without 52.8462 basic threshold 71.04 + WCA Result : basic_violation 1 rule_violation 0 preventive_action_index 0 diff --git a/wca-integration/src/test/resources/domains/wca_uncertainties.txt b/wca-integration/src/test/resources/domains/wca_uncertainties.txt new file mode 100644 index 00000000..8042c461 --- /dev/null +++ b/wca-integration/src/test/resources/domains/wca_uncertainties.txt @@ -0,0 +1,3 @@ +"LOAD_1";-1.1 +"LOAD_2";1.5 +"GEN_1";1.2 diff --git a/wca-integration/src/test/resources/simplelogger.properties b/wca-integration/src/test/resources/simplelogger.properties new file mode 100644 index 00000000..b4bb306d --- /dev/null +++ b/wca-integration/src/test/resources/simplelogger.properties @@ -0,0 +1,10 @@ +# Copyright (c) 2017, 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/. + +org.slf4j.simpleLogger.log.eu.itesla_project.wca.report.WCAReportImpl=off +org.slf4j.simpleLogger.log.eu.itesla_project.wca.WCAFilteredClusters=off +org.slf4j.simpleLogger.log.eu.itesla_project.wca.WCAUtils=off +org.slf4j.simpleLogger.log.eu.itesla_project.wca.SimpleContingencyDbFacade=off +org.slf4j.simpleLogger.log.eu.itesla_project.wca.WCAImpl=off \ No newline at end of file