From e256ee2d21ddfa53793fae41b99befdcd9bede0b Mon Sep 17 00:00:00 2001 From: Mathieu BAGUE Date: Wed, 30 Aug 2017 21:32:40 +0200 Subject: [PATCH] Improve security-analysis results (#199) Improve security-analysis results --- .../loadflow/LoadFlowActionSimulator.java | 6 +- .../AbstractLoadFlowRulesEngineTest.java | 2 +- commons/pom.xml | 5 + .../itesla_project/commons/json/JsonUtil.java | 59 +++++ .../json/ContingencyElementDeserializer.java | 2 + .../json/ContingencyElementSerializer.java | 38 +++ .../contingency/json/ContingencyJsonTest.java | 7 +- .../src/test/resources/contingency.json | 12 +- .../iidm/network/TwoTerminalsConnectable.java | 2 + .../iidm/network/impl/AbstractBranch.java | 12 + .../iidm/network/impl/LineTest.java | 244 +++++++++--------- .../security/LimitViolation.java | 4 +- .../security/LimitViolationFilter.java | 8 +- .../eu/itesla_project/security/Security.java | 169 ++++++------ .../security/SecurityAnalysisImpl.java | 2 +- .../security/SecurityAnalysisTool.java | 31 ++- .../json/LimitViolationDeserializer.java | 14 +- .../json/LimitViolationSerializer.java | 41 +++ .../SecurityAnalysisResultDeserializer.java | 28 +- .../SecurityAnalysisResultSerializer.java | 14 +- .../itesla_project/security/SecurityTest.java | 52 ++-- .../json/SecurityAnalysisResultJsonTest.java | 9 +- .../resources/SecurityAnalysisResult.json | 40 +-- 23 files changed, 484 insertions(+), 317 deletions(-) create mode 100644 commons/src/main/java/eu/itesla_project/commons/json/JsonUtil.java create mode 100644 contingency-api/src/main/java/eu/itesla_project/contingency/json/ContingencyElementSerializer.java create mode 100644 security-analysis/src/main/java/eu/itesla_project/security/json/LimitViolationSerializer.java diff --git a/action/action-simulator/src/main/java/eu/itesla_project/action/simulator/loadflow/LoadFlowActionSimulator.java b/action/action-simulator/src/main/java/eu/itesla_project/action/simulator/loadflow/LoadFlowActionSimulator.java index ef84b4b5..bc24ef0c 100644 --- a/action/action-simulator/src/main/java/eu/itesla_project/action/simulator/loadflow/LoadFlowActionSimulator.java +++ b/action/action-simulator/src/main/java/eu/itesla_project/action/simulator/loadflow/LoadFlowActionSimulator.java @@ -117,7 +117,7 @@ public void start(ActionDb actionDb, List contingencyIds) { } } - protected LoadFlowFactory newLoadLoadLowFactory() { + protected LoadFlowFactory newLoadFlowFactory() { try { return config.getLoadFlowFactoryClass().newInstance(); } catch (InstantiationException e) { @@ -136,7 +136,7 @@ private boolean next(ActionDb actionDb, RunningContext context) { observers.forEach(o -> o.roundBegin(context.getContingency(), context.getRound())); } - LoadFlowFactory loadFlowFactory = newLoadLoadLowFactory(); + LoadFlowFactory loadFlowFactory = newLoadFlowFactory(); LoadFlow loadFlow = loadFlowFactory.create(context.getNetwork(), computationManager, 0); LOGGER.info("Running loadflow ({})", loadFlow.getName()); @@ -147,7 +147,7 @@ private boolean next(ActionDb actionDb, RunningContext context) { throw new RuntimeException(e); } if (result.isOk()) { - List violations = LIMIT_VIOLATION_FILTER.apply(Security.checkLimits(context.getNetwork(), Security.CurrentLimitType.TATL, 1)); + List violations = LIMIT_VIOLATION_FILTER.apply(Security.checkLimits(context.getNetwork(), 1)); if (violations.size() > 0) { LOGGER.info("Violations: \n{}", Security.printLimitsViolations(violations, NO_FILTER)); } diff --git a/action/action-simulator/src/test/java/eu/itesla_project/action/simulator/AbstractLoadFlowRulesEngineTest.java b/action/action-simulator/src/test/java/eu/itesla_project/action/simulator/AbstractLoadFlowRulesEngineTest.java index 8c9f1c91..8104e7a1 100644 --- a/action/action-simulator/src/test/java/eu/itesla_project/action/simulator/AbstractLoadFlowRulesEngineTest.java +++ b/action/action-simulator/src/test/java/eu/itesla_project/action/simulator/AbstractLoadFlowRulesEngineTest.java @@ -64,7 +64,7 @@ public void setUp() throws Exception { actionDb = new ActionDslLoader(src).load(network); engine = new LoadFlowActionSimulator(network, computationManager, new LoadFlowActionSimulatorConfig(LoadFlowFactory.class, 3, false), observer) { @Override - protected LoadFlowFactory newLoadLoadLowFactory() { + protected LoadFlowFactory newLoadFlowFactory() { return loadFlowFactory; } }; diff --git a/commons/pom.xml b/commons/pom.xml index 78971f28..c3f3474c 100644 --- a/commons/pom.xml +++ b/commons/pom.xml @@ -39,6 +39,11 @@ + + + com.fasterxml.jackson.core + jackson-databind + com.google.auto.service auto-service diff --git a/commons/src/main/java/eu/itesla_project/commons/json/JsonUtil.java b/commons/src/main/java/eu/itesla_project/commons/json/JsonUtil.java new file mode 100644 index 00000000..0f3dbaa2 --- /dev/null +++ b/commons/src/main/java/eu/itesla_project/commons/json/JsonUtil.java @@ -0,0 +1,59 @@ +/** + * 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.commons.json; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.base.Strings; + +import java.io.IOException; +import java.util.Objects; + +/** + * @author Mathieu Bague + */ +public final class JsonUtil { + + private JsonUtil() { + } + + public static void writeOptionalStringField(JsonGenerator jsonGenerator, String fieldName, String value) throws IOException { + Objects.requireNonNull(jsonGenerator); + Objects.requireNonNull(fieldName); + + if (!Strings.isNullOrEmpty(value)) { + jsonGenerator.writeStringField(fieldName, value); + } + } + + public static void writeOptionalEnumField(JsonGenerator jsonGenerator, String fieldName, Enum value) throws IOException { + Objects.requireNonNull(jsonGenerator); + Objects.requireNonNull(fieldName); + + if (value != null) { + jsonGenerator.writeStringField(fieldName, value.name()); + } + } + + public static void writeOptionalFloatField(JsonGenerator jsonGenerator, String fieldName, float value) throws IOException { + Objects.requireNonNull(jsonGenerator); + Objects.requireNonNull(fieldName); + + if (!Float.isNaN(value)) { + jsonGenerator.writeNumberField(fieldName, value); + } + } + + public static void writeOptionalIntegerField(JsonGenerator jsonGenerator, String fieldName, int value) throws IOException { + Objects.requireNonNull(jsonGenerator); + Objects.requireNonNull(fieldName); + + if (value != Integer.MAX_VALUE) { + jsonGenerator.writeNumberField(fieldName, value); + } + } + +} diff --git a/contingency-api/src/main/java/eu/itesla_project/contingency/json/ContingencyElementDeserializer.java b/contingency-api/src/main/java/eu/itesla_project/contingency/json/ContingencyElementDeserializer.java index 76109e05..540811ab 100644 --- a/contingency-api/src/main/java/eu/itesla_project/contingency/json/ContingencyElementDeserializer.java +++ b/contingency-api/src/main/java/eu/itesla_project/contingency/json/ContingencyElementDeserializer.java @@ -52,6 +52,8 @@ public ContingencyElement deserialize(JsonParser parser, DeserializationContext if (type != null) { switch (type) { case LINE: + return new LineContingency(id, voltageLevelId); + case BRANCH: return new BranchContingency(id, voltageLevelId); diff --git a/contingency-api/src/main/java/eu/itesla_project/contingency/json/ContingencyElementSerializer.java b/contingency-api/src/main/java/eu/itesla_project/contingency/json/ContingencyElementSerializer.java new file mode 100644 index 00000000..5a39f661 --- /dev/null +++ b/contingency-api/src/main/java/eu/itesla_project/contingency/json/ContingencyElementSerializer.java @@ -0,0 +1,38 @@ +/** + * 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.contingency.json; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import eu.itesla_project.commons.json.JsonUtil; +import eu.itesla_project.contingency.BranchContingency; +import eu.itesla_project.contingency.ContingencyElement; + +import java.io.IOException; + +/** + * @author Mathieu Bague + */ +public class ContingencyElementSerializer extends StdSerializer { + + public ContingencyElementSerializer() { + super(ContingencyElement.class); + } + + @Override + public void serialize(ContingencyElement contingencyElement, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeStartObject(); + jsonGenerator.writeStringField("id", contingencyElement.getId()); + jsonGenerator.writeStringField("type", contingencyElement.getType().name()); + if (contingencyElement instanceof BranchContingency) { + BranchContingency branchContingencyElement = (BranchContingency) contingencyElement; + JsonUtil.writeOptionalStringField(jsonGenerator, "voltageLevelId", branchContingencyElement.getVoltageLevelId()); + } + jsonGenerator.writeEndObject(); + } +} diff --git a/contingency-api/src/test/java/eu/itesla_project/contingency/json/ContingencyJsonTest.java b/contingency-api/src/test/java/eu/itesla_project/contingency/json/ContingencyJsonTest.java index cbf2f8b5..68fac60a 100644 --- a/contingency-api/src/test/java/eu/itesla_project/contingency/json/ContingencyJsonTest.java +++ b/contingency-api/src/test/java/eu/itesla_project/contingency/json/ContingencyJsonTest.java @@ -23,6 +23,8 @@ private static Contingency create() { List elements = new ArrayList<>(); elements.add(new BranchContingency("NHV1_NHV2_2", "VLHV1")); elements.add(new BranchContingency("NHV1_NHV2_1")); + elements.add(new LineContingency("NHV1_NHV2_2", "VLHV1")); + elements.add(new LineContingency("NHV1_NHV2_2")); elements.add(new GeneratorContingency("GEN")); elements.add(new BusbarSectionContingency("BBS1")); @@ -51,8 +53,11 @@ private static void write(Contingency object, Path jsonFile) { try (OutputStream os = Files.newOutputStream(jsonFile)) { ObjectMapper mapper = new ObjectMapper(); - ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter(); + SimpleModule module = new SimpleModule(); + module.addSerializer(ContingencyElement.class, new ContingencyElementSerializer()); + mapper.registerModule(module); + ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter(); writer.writeValue(os, object); } catch (IOException e) { throw new UncheckedIOException(e); diff --git a/contingency-api/src/test/resources/contingency.json b/contingency-api/src/test/resources/contingency.json index cd51e903..e8aa78d6 100644 --- a/contingency-api/src/test/resources/contingency.json +++ b/contingency-api/src/test/resources/contingency.json @@ -2,12 +2,18 @@ "id" : "contingency", "elements" : [ { "id" : "NHV1_NHV2_2", - "voltageLevelId" : "VLHV1", - "type" : "BRANCH" + "type" : "BRANCH", + "voltageLevelId" : "VLHV1" }, { "id" : "NHV1_NHV2_1", - "voltageLevelId" : null, "type" : "BRANCH" + }, { + "id" : "NHV1_NHV2_2", + "type" : "LINE", + "voltageLevelId" : "VLHV1" + }, { + "id" : "NHV1_NHV2_2", + "type" : "LINE" }, { "id" : "GEN", "type" : "GENERATOR" diff --git a/iidm/iidm-api/src/main/java/eu/itesla_project/iidm/network/TwoTerminalsConnectable.java b/iidm/iidm-api/src/main/java/eu/itesla_project/iidm/network/TwoTerminalsConnectable.java index 97b6e0a0..df86ce50 100644 --- a/iidm/iidm-api/src/main/java/eu/itesla_project/iidm/network/TwoTerminalsConnectable.java +++ b/iidm/iidm-api/src/main/java/eu/itesla_project/iidm/network/TwoTerminalsConnectable.java @@ -40,6 +40,8 @@ interface Overload { Terminal getTerminal(String voltageLevelId); + CurrentLimits getCurrentLimits(Side side); + CurrentLimits getCurrentLimits1(); CurrentLimitsAdder newCurrentLimits1(); diff --git a/iidm/iidm-impl/src/main/java/eu/itesla_project/iidm/network/impl/AbstractBranch.java b/iidm/iidm-impl/src/main/java/eu/itesla_project/iidm/network/impl/AbstractBranch.java index 004bf6d8..2104e326 100644 --- a/iidm/iidm-impl/src/main/java/eu/itesla_project/iidm/network/impl/AbstractBranch.java +++ b/iidm/iidm-impl/src/main/java/eu/itesla_project/iidm/network/impl/AbstractBranch.java @@ -75,6 +75,18 @@ public void setCurrentLimits(Branch.Side side, CurrentLimitsImpl limits) { } } + public CurrentLimits getCurrentLimits(Side side) { + switch (side) { + case ONE: + return limits1; + case TWO: + return limits2; + default: + throw new InternalError(); + } + + } + public CurrentLimits getCurrentLimits1() { return limits1; } diff --git a/iidm/iidm-impl/src/test/java/eu/itesla_project/iidm/network/impl/LineTest.java b/iidm/iidm-impl/src/test/java/eu/itesla_project/iidm/network/impl/LineTest.java index 27cc784f..540d8406 100644 --- a/iidm/iidm-impl/src/test/java/eu/itesla_project/iidm/network/impl/LineTest.java +++ b/iidm/iidm-impl/src/test/java/eu/itesla_project/iidm/network/impl/LineTest.java @@ -36,21 +36,21 @@ public void setUp() { public void baseAcLineTests() { // adder Line acLine = network.newLine() - .setId("line") - .setName("lineName") - .setR(1.0f) - .setX(2.0f) - .setG1(3.0f) - .setG2(3.5f) - .setB1(4.0f) - .setB2(4.5f) - .setVoltageLevel1("vl1") - .setVoltageLevel2("vl2") - .setBus1("busA") - .setBus2("busB") - .setConnectableBus1("busA") - .setConnectableBus2("busB") - .add(); + .setId("line") + .setName("lineName") + .setR(1.0f) + .setX(2.0f) + .setG1(3.0f) + .setG2(3.5f) + .setB1(4.0f) + .setB2(4.5f) + .setVoltageLevel1("vl1") + .setVoltageLevel2("vl2") + .setBus1("busA") + .setBus2("busB") + .setConnectableBus1("busA") + .setConnectableBus2("busB") + .add(); assertEquals("line", acLine.getId()); assertEquals("lineName", acLine.getName()); assertEquals(1.0f, acLine.getR(), 0.0f); @@ -94,23 +94,25 @@ public void baseAcLineTests() { assertFalse(acLine.isTieLine()); CurrentLimits currentLimits1 = acLine.newCurrentLimits1() - .setPermanentLimit(100) - .beginTemporaryLimit() - .setName("5'") - .setAcceptableDuration(5 * 60) - .setValue(1400) - .endTemporaryLimit() - .add(); + .setPermanentLimit(100) + .beginTemporaryLimit() + .setName("5'") + .setAcceptableDuration(5 * 60) + .setValue(1400) + .endTemporaryLimit() + .add(); CurrentLimits currentLimits2 = acLine.newCurrentLimits2() - .setPermanentLimit(50) - .beginTemporaryLimit() - .setName("20'") - .setAcceptableDuration(20 * 60) - .setValue(1200) - .endTemporaryLimit() - .add(); + .setPermanentLimit(50) + .beginTemporaryLimit() + .setName("20'") + .setAcceptableDuration(20 * 60) + .setValue(1200) + .endTemporaryLimit() + .add(); assertSame(currentLimits1, acLine.getCurrentLimits1()); assertSame(currentLimits2, acLine.getCurrentLimits2()); + assertSame(currentLimits1, acLine.getCurrentLimits(Branch.Side.ONE)); + assertSame(currentLimits2, acLine.getCurrentLimits(Branch.Side.TWO)); // add power on line Terminal terminal1 = acLine.getTerminal1(); @@ -216,36 +218,36 @@ public void testTieLineAdder() { // adder TieLine tieLine = network.newTieLine().setId("testTie") - .setName("testNameTie") - .setVoltageLevel1("vl1") - .setBus1("busA") - .setConnectableBus1("busA") - .setVoltageLevel2("vl2") - .setBus2("busB") - .setConnectableBus2("busB") - .setUcteXnodeCode("ucte") - .line1() - .setId("hl1") - .setName("half1_name") - .setR(r) - .setX(x) - .setB1(hl1b1) - .setB2(hl1b2) - .setG1(hl1g1) - .setG2(hl1g2) - .setXnodeQ(xnodeQ) - .setXnodeP(xnodeP) - .line2() - .setId("hl2") - .setR(r2) - .setX(x2) - .setB1(hl2b1) - .setB2(hl2b2) - .setG1(hl2g1) - .setG2(hl2g2) - .setXnodeP(xnodeP) - .setXnodeQ(xnodeQ) - .add(); + .setName("testNameTie") + .setVoltageLevel1("vl1") + .setBus1("busA") + .setConnectableBus1("busA") + .setVoltageLevel2("vl2") + .setBus2("busB") + .setConnectableBus2("busB") + .setUcteXnodeCode("ucte") + .line1() + .setId("hl1") + .setName("half1_name") + .setR(r) + .setX(x) + .setB1(hl1b1) + .setB2(hl1b2) + .setG1(hl1g1) + .setG2(hl1g2) + .setXnodeQ(xnodeQ) + .setXnodeP(xnodeP) + .line2() + .setId("hl2") + .setR(r2) + .setX(x2) + .setB1(hl2b1) + .setB2(hl2b2) + .setG1(hl2g1) + .setG2(hl2g2) + .setXnodeP(xnodeP) + .setXnodeQ(xnodeQ) + .add(); assertTrue(tieLine.isTieLine()); assertEquals(ConnectableType.LINE, tieLine.getType()); assertEquals("ucte", tieLine.getUcteXnodeCode()); @@ -305,7 +307,7 @@ public void invalidHalfLineCharacteristicsR() { thrown.expect(ValidationException.class); thrown.expectMessage("r is not set for half line"); createTieLineWithHalfline2ByDefault("invalid", "invalid", "invalid", Float.NaN, 2.0f, - 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "code"); + 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "code"); } @Test @@ -313,7 +315,7 @@ public void invalidHalfLineCharacteristicsX() { thrown.expect(ValidationException.class); thrown.expectMessage("x is not set for half line"); createTieLineWithHalfline2ByDefault("invalid", "invalid", "invalid", 1.0f, Float.NaN, - 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "code"); + 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "code"); } @Test @@ -321,7 +323,7 @@ public void invalidHalfLineCharacteristicsG1() { thrown.expect(ValidationException.class); thrown.expectMessage("g1 is not set for half line"); createTieLineWithHalfline2ByDefault("invalid", "invalid", "invalid", 1.0f, 2.0f, - Float.NaN, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "code"); + Float.NaN, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "code"); } @Test @@ -329,7 +331,7 @@ public void invalidHalfLineCharacteristicsG2() { thrown.expect(ValidationException.class); thrown.expectMessage("g2 is not set for half line"); createTieLineWithHalfline2ByDefault("invalid", "invalid", "invalid", 1.0f, 2.0f, - 3.0f, Float.NaN, 4.0f, 4.5f, 5.0f, 6.0f, "code"); + 3.0f, Float.NaN, 4.0f, 4.5f, 5.0f, 6.0f, "code"); } @Test @@ -337,7 +339,7 @@ public void invalidHalfLineCharacteristicsB1() { thrown.expect(ValidationException.class); thrown.expectMessage("b1 is not set for half line"); createTieLineWithHalfline2ByDefault("invalid", "invalid", "invalid", 1.0f, 2.0f, - 3.0f, 3.5f, Float.NaN, 4.5f, 5.0f, 6.0f, "code"); + 3.0f, 3.5f, Float.NaN, 4.5f, 5.0f, 6.0f, "code"); } @Test @@ -345,7 +347,7 @@ public void invalidHalfLineCharacteristicsB2() { thrown.expect(ValidationException.class); thrown.expectMessage("b2 is not set for half line"); createTieLineWithHalfline2ByDefault("invalid", "invalid", "invalid", 1.0f, 2.0f, - 3.0f, 3.5f, 4.0f, Float.NaN, 5.0f, 6.0f, "code"); + 3.0f, 3.5f, 4.0f, Float.NaN, 5.0f, 6.0f, "code"); } @Test @@ -353,7 +355,7 @@ public void invalidHalfLineCharacteristicsP() { thrown.expect(ValidationException.class); thrown.expectMessage("xnodeP is not set for half line"); createTieLineWithHalfline2ByDefault("invalid", "invalid", "invalid", 1.0f, 2.0f, - 3.0f, 3.5f, 4.0f, 4.5f, Float.NaN, 6.0f, "code"); + 3.0f, 3.5f, 4.0f, 4.5f, Float.NaN, 6.0f, "code"); } @Test @@ -361,7 +363,7 @@ public void invalidHalfLineCharacteristicsQ() { thrown.expect(ValidationException.class); thrown.expectMessage("xnodeQ is not set for half line"); createTieLineWithHalfline2ByDefault("invalid", "invalid", "invalid", 1.0f, 2.0f, - 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, Float.NaN, "code"); + 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, Float.NaN, "code"); } @Test @@ -369,7 +371,7 @@ public void halfLineIdNull() { thrown.expect(ValidationException.class); thrown.expectMessage("id is not set for half line"); createTieLineWithHalfline2ByDefault("invalid", "invalid", null, 1.0f, 2.0f, - 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "code"); + 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "code"); } @Test @@ -377,22 +379,22 @@ public void uctecodeNull() { thrown.expect(ValidationException.class); thrown.expectMessage("ucteXnodeCode is not set"); createTieLineWithHalfline2ByDefault("invalid", "invalid", "invalid", 1.0f, 2.0f, - 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, null); + 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, null); } @Test public void duplicate() { createTieLineWithHalfline2ByDefault("duplicate", "duplicate", "id1", 1.0f, 2.0f, - 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "duplicate"); + 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "duplicate"); thrown.expect(ITeslaException.class); createTieLineWithHalfline2ByDefault("duplicate", "duplicate", "id1", 1.0f, 2.0f, - 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "duplicate"); + 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "duplicate"); } @Test public void testRemove() { createTieLineWithHalfline2ByDefault("toRemove", "toRemove", "id1", 1.0f, 2.0f, - 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "toRemove"); + 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, "toRemove"); Line line = network.getLine("toRemove"); assertNotNull(line); assertTrue(line.isTieLine()); @@ -406,58 +408,58 @@ public void testRemove() { private void createLineBetweenVoltageAB(String id, String name, float r, float x, float g1, float g2, float b1, float b2) { network.newLine() - .setId(id) - .setName(name) - .setR(r) - .setX(x) - .setG1(g1) - .setG2(g2) - .setB1(b1) - .setB2(b2) - .setVoltageLevel1("vl1") - .setVoltageLevel2("vl2") - .setBus1("busA") - .setBus2("busB") - .setConnectableBus1("busA") - .setConnectableBus2("busB") - .add(); + .setId(id) + .setName(name) + .setR(r) + .setX(x) + .setG1(g1) + .setG2(g2) + .setB1(b1) + .setB2(b2) + .setVoltageLevel1("vl1") + .setVoltageLevel2("vl2") + .setBus1("busA") + .setBus2("busB") + .setConnectableBus1("busA") + .setConnectableBus2("busB") + .add(); } private void createTieLineWithHalfline2ByDefault(String id, String name, String halfLineId, float r, float x, - float g1, float g2, float b1, float b2, - float xnodeP, float xnodeQ, String code) { + float g1, float g2, float b1, float b2, + float xnodeP, float xnodeQ, String code) { network.newTieLine() - .setId(id) - .setName(name) - .line1() - .setId(halfLineId) - .setName("half1_name") - .setR(r) - .setX(x) - .setB1(b1) - .setB2(b2) - .setG1(g1) - .setG2(g2) - .setXnodeQ(xnodeQ) - .setXnodeP(xnodeP) - .line2() - .setId("hl2") - .setName("half2_name") - .setR(1.0f) - .setX(2.0f) - .setB1(3.0f) - .setB2(3.5f) - .setG1(4.0f) - .setG2(4.5f) - .setXnodeP(5.0f) - .setXnodeQ(6.0f) - .setVoltageLevel1("vl1") - .setBus1("busA") - .setConnectableBus1("busA") - .setVoltageLevel2("vl2") - .setBus2("busB") - .setConnectableBus2("busB") - .setUcteXnodeCode(code) - .add(); + .setId(id) + .setName(name) + .line1() + .setId(halfLineId) + .setName("half1_name") + .setR(r) + .setX(x) + .setB1(b1) + .setB2(b2) + .setG1(g1) + .setG2(g2) + .setXnodeQ(xnodeQ) + .setXnodeP(xnodeP) + .line2() + .setId("hl2") + .setName("half2_name") + .setR(1.0f) + .setX(2.0f) + .setB1(3.0f) + .setB2(3.5f) + .setG1(4.0f) + .setG2(4.5f) + .setXnodeP(5.0f) + .setXnodeQ(6.0f) + .setVoltageLevel1("vl1") + .setBus1("busA") + .setConnectableBus1("busA") + .setVoltageLevel2("vl2") + .setBus2("busB") + .setConnectableBus2("busB") + .setUcteXnodeCode(code) + .add(); } } diff --git a/security-analysis/src/main/java/eu/itesla_project/security/LimitViolation.java b/security-analysis/src/main/java/eu/itesla_project/security/LimitViolation.java index 15fc8f23..427c34ed 100644 --- a/security-analysis/src/main/java/eu/itesla_project/security/LimitViolation.java +++ b/security-analysis/src/main/java/eu/itesla_project/security/LimitViolation.java @@ -7,10 +7,10 @@ */ package eu.itesla_project.security; -import java.util.Objects; - import eu.itesla_project.iidm.network.Country; +import java.util.Objects; + /** * @author Geoffroy Jamgotchian */ diff --git a/security-analysis/src/main/java/eu/itesla_project/security/LimitViolationFilter.java b/security-analysis/src/main/java/eu/itesla_project/security/LimitViolationFilter.java index 1d72b427..db31728e 100644 --- a/security-analysis/src/main/java/eu/itesla_project/security/LimitViolationFilter.java +++ b/security-analysis/src/main/java/eu/itesla_project/security/LimitViolationFilter.java @@ -7,14 +7,14 @@ */ package eu.itesla_project.security; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - import eu.itesla_project.commons.config.ModuleConfig; import eu.itesla_project.commons.config.PlatformConfig; import eu.itesla_project.iidm.network.Country; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + /** * @author Geoffroy Jamgotchian */ diff --git a/security-analysis/src/main/java/eu/itesla_project/security/Security.java b/security-analysis/src/main/java/eu/itesla_project/security/Security.java index a4b49a40..55955cad 100644 --- a/security-analysis/src/main/java/eu/itesla_project/security/Security.java +++ b/security-analysis/src/main/java/eu/itesla_project/security/Security.java @@ -36,97 +36,78 @@ public enum CurrentLimitType { private Security() { } - private static Country getCountry(Branch branch, Terminal terminal) { - return terminal == branch.getTerminal1() ? branch.getTerminal1().getVoltageLevel().getSubstation().getCountry() - : branch.getTerminal2().getVoltageLevel().getSubstation().getCountry(); + private static Country getCountry(Branch branch, Branch.Side side) { + return branch.getTerminal(side).getVoltageLevel().getSubstation().getCountry(); } - private static float getBaseVoltage(Branch branch) { + private static float getNominalVoltage(Branch branch) { return Math.max(branch.getTerminal1().getVoltageLevel().getNominalV(), branch.getTerminal2().getVoltageLevel().getNominalV()); } - private static void checkCurrentLimits(Iterable branches, CurrentLimitType currentLimitType, - float limitReduction, List violations) { - for (Branch branch : branches) { - switch (currentLimitType) { - case PATL: - if (branch.checkPermanentLimit1(limitReduction)) { - violations.add(new LimitViolation(branch.getId(), - LimitViolationType.CURRENT, - branch.getCurrentLimits1().getPermanentLimit(), - PERMANENT_LIMIT_NAME, - limitReduction, - branch.getTerminal1().getI(), - getCountry(branch, branch.getTerminal1()), - getBaseVoltage(branch))); - } - if (branch.checkPermanentLimit2(limitReduction)) { - violations.add(new LimitViolation(branch.getId(), - LimitViolationType.CURRENT, - branch.getCurrentLimits2().getPermanentLimit(), - PERMANENT_LIMIT_NAME, - limitReduction, - branch.getTerminal2().getI(), - getCountry(branch, branch.getTerminal2()), - getBaseVoltage(branch))); - } - break; - - case TATL: - Branch.Overload o1 = branch.checkTemporaryLimits1(limitReduction); - if (o1 != null) { - violations.add(new LimitViolation(branch.getId(), - LimitViolationType.CURRENT, - o1.getPreviousLimit(), - o1.getTemporaryLimit().getName(), - limitReduction, - branch.getTerminal1().getI(), - getCountry(branch, branch.getTerminal1()), - getBaseVoltage(branch))); - } - Branch.Overload o2 = branch.checkTemporaryLimits2(limitReduction); - if (o2 != null) { - violations.add(new LimitViolation(branch.getId(), - LimitViolationType.CURRENT, - o2.getPreviousLimit(), - o2.getTemporaryLimit().getName(), - limitReduction, - branch.getTerminal2().getI(), - getCountry(branch, branch.getTerminal2()), - getBaseVoltage(branch))); - } - break; + private static float getNominalVoltage(Branch branch, Branch.Side side) { + return branch.getTerminal(side).getVoltageLevel().getNominalV(); + } - default: - throw new AssertionError(); - } + public static String getLimitName(int acceptableDuration) { + if (acceptableDuration == Integer.MAX_VALUE) { + return PERMANENT_LIMIT_NAME; + } else { + return String.format("Overload %d'", acceptableDuration / 60); } } - public static List checkLimits(Network network) { - return checkLimits(network, CurrentLimitType.PATL, 1f); + private static void checkPermanentLimit(Branch branch, Branch.Side side, float limitReduction, List violations) { + if (branch.checkPermanentLimit(side, limitReduction)) { + violations.add(new LimitViolation(branch.getId(), + LimitViolationType.CURRENT, + branch.getCurrentLimits(side).getPermanentLimit(), + PERMANENT_LIMIT_NAME, + limitReduction, + branch.getTerminal(side).getI(), + getCountry(branch, side), + getNominalVoltage(branch, side))); + } } - public static List checkLimits(Network network, float limitReduction) { - List violations = new ArrayList<>(); - for (CurrentLimitType type : CurrentLimitType.values()) { - violations.addAll(checkLimits(network, type, limitReduction)); + private static void checkCurrentLimits(Branch branch, Branch.Side side, float limitReduction, List violations) { + Branch.Overload o1 = branch.checkTemporaryLimits(side, limitReduction); + if (o1 != null) { + violations.add(new LimitViolation(branch.getId(), + LimitViolationType.CURRENT, + o1.getPreviousLimit(), + getLimitName(o1.getTemporaryLimit().getAcceptableDuration()), + limitReduction, + branch.getTerminal(side).getI(), + getCountry(branch, side), + getNominalVoltage(branch, side))); + } else { + checkPermanentLimit(branch, side, limitReduction, violations); } - return violations; } - public static List checkLimits(Network network, CurrentLimitType currentLimitType, float limitReduction) { + private static void checkCurrentLimits(Iterable branches, + float limitReduction, List violations) { + for (Branch branch : branches) { + checkCurrentLimits(branch, Branch.Side.ONE, limitReduction, violations); + checkCurrentLimits(branch, Branch.Side.TWO, limitReduction, violations); + } + } + + public static List checkLimits(Network network) { + return checkLimits(network, 1f); + } + + public static List checkLimits(Network network, float limitReduction) { Objects.requireNonNull(network); - Objects.requireNonNull(currentLimitType); //if (limitReduction <= 0 || limitReduction > 1) { // allow to increase the limits if (limitReduction <= 0) { throw new IllegalArgumentException("Bad limit reduction " + limitReduction); } List violations = new ArrayList<>(); - checkCurrentLimits(network.getLines(), currentLimitType, limitReduction, violations); - checkCurrentLimits(network.getTwoWindingsTransformers(), currentLimitType, limitReduction, violations); + checkCurrentLimits(network.getLines(), limitReduction, violations); + checkCurrentLimits(network.getTwoWindingsTransformers(), limitReduction, violations); for (VoltageLevel vl : network.getVoltageLevels()) { if (!Float.isNaN(vl.getLowVoltageLimit())) { for (Bus b : vl.getBusView().getBuses()) { @@ -169,27 +150,27 @@ public static String printLimitsViolations(List violations, Limi Objects.requireNonNull(filter); List filteredViolations = filter.apply(violations); if (filteredViolations.size() > 0) { - Collections.sort(filteredViolations, (o1, o2) -> o1.getSubjectId().compareTo(o2.getSubjectId())); + filteredViolations.sort(Comparator.comparing(LimitViolation::getSubjectId)); Table table = new Table(9, BorderStyle.CLASSIC_WIDE); table.addCell("Country"); table.addCell("Base voltage"); table.addCell("Equipment (" + filteredViolations.size() + ")"); table.addCell("Violation type"); table.addCell("Violation name"); - table.addCell("value"); - table.addCell("limit"); + table.addCell("Value"); + table.addCell("Limit"); table.addCell("abs(value-limit)"); - table.addCell("charge %"); + table.addCell("Loading rate %"); for (LimitViolation violation : filteredViolations) { table.addCell(violation.getCountry() != null ? violation.getCountry().name() : ""); table.addCell(Float.isNaN(violation.getBaseVoltage()) ? "" : Float.toString(violation.getBaseVoltage())); table.addCell(violation.getSubjectId()); table.addCell(violation.getLimitType().name()); - table.addCell(Objects.toString(violation.getLimitName(), "")); + table.addCell(getViolationName(violation)); table.addCell(Float.toString(violation.getValue())); - table.addCell(Float.toString(violation.getLimit()) + (violation.getLimitReduction() != 1f ? " * " + violation.getLimitReduction() : "")); + table.addCell(getViolationLimit(violation)); table.addCell(Float.toString(Math.abs(violation.getValue() - violation.getLimit() * violation.getLimitReduction()))); - table.addCell(Integer.toString(Math.round(Math.abs(violation.getValue()) / violation.getLimit() * 100f))); + table.addCell(Integer.toString(getViolationValue(violation))); } return table.render(); } @@ -215,7 +196,7 @@ public static void printPreContingencyViolations(SecurityAnalysisResult result, new Column("Violation name"), new Column("Value"), new Column("Limit"), - new Column("Charge %"))) { + new Column("Loading rate %"))) { for (String action : result.getPreContingencyResult().getActionsTaken()) { formatter.writeCell(action) .writeEmptyCell() @@ -229,16 +210,16 @@ public static void printPreContingencyViolations(SecurityAnalysisResult result, ? limitViolationFilter.apply(result.getPreContingencyResult().getLimitViolations()) : result.getPreContingencyResult().getLimitViolations(); filteredLimitViolations.stream() - .sorted((o1, o2) -> o1.getSubjectId().compareTo(o2.getSubjectId())) + .sorted(Comparator.comparing(LimitViolation::getSubjectId)) .forEach(violation -> { try { formatter.writeEmptyCell() .writeCell(violation.getSubjectId()) .writeCell(violation.getLimitType().name()) - .writeCell(Objects.toString(violation.getLimitName(), "")) + .writeCell(getViolationName(violation)) .writeCell(violation.getValue()) - .writeCell(Float.toString(violation.getLimit()) + (violation.getLimitReduction() != 1f ? " * " + violation.getLimitReduction() : "")) - .writeCell(Math.round(Math.abs(violation.getValue()) / violation.getLimit() * 100f)); + .writeCell(getViolationLimit(violation)) + .writeCell(getViolationValue(violation)); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -248,6 +229,18 @@ public static void printPreContingencyViolations(SecurityAnalysisResult result, } } + private static String getViolationName(LimitViolation violation) { + return Objects.toString(violation.getLimitName(), ""); + } + + private static String getViolationLimit(LimitViolation violation) { + return Float.toString(violation.getLimit()) + (violation.getLimitReduction() != 1f ? " * " + violation.getLimitReduction() : ""); + } + + private static int getViolationValue(LimitViolation violation) { + return Math.round(Math.abs(violation.getValue()) / violation.getLimit() * 100f); + } + /** * Used to identify a limit violation to avoid duplicated violation between pre and post contingency analysis */ @@ -316,7 +309,7 @@ public static void printPostContingencyViolations(SecurityAnalysisResult result, new Column("Violation name"), new Column("Value"), new Column("Limit"), - new Column("Charge %"))) { + new Column("Loading rate %"))) { result.getPostContingencyResults() .stream() .sorted(Comparator.comparing(o2 -> o2.getContingency().getId())) @@ -324,8 +317,8 @@ public static void printPostContingencyViolations(SecurityAnalysisResult result, try { // configured filtering List filteredLimitViolations = limitViolationFilter != null - ? limitViolationFilter.apply(postContingencyResult.getLimitViolations()) - : postContingencyResult.getLimitViolations(); + ? limitViolationFilter.apply(postContingencyResult.getLimitViolationsResult().getLimitViolations()) + : postContingencyResult.getLimitViolationsResult().getLimitViolations(); // pre-contingency violations filtering List filteredLimitViolations2 = filteredLimitViolations.stream() @@ -356,7 +349,7 @@ public static void printPostContingencyViolations(SecurityAnalysisResult result, } filteredLimitViolations2.stream() - .sorted(Comparator.comparing(o -> o.getSubjectId())) + .sorted(Comparator.comparing(LimitViolation::getSubjectId)) .forEach(violation -> { try { formatter.writeEmptyCell() @@ -364,10 +357,10 @@ public static void printPostContingencyViolations(SecurityAnalysisResult result, .writeEmptyCell() .writeCell(violation.getSubjectId()) .writeCell(violation.getLimitType().name()) - .writeCell(Objects.toString(violation.getLimitName(), "")) + .writeCell(getViolationName(violation)) .writeCell(violation.getValue()) - .writeCell(Float.toString(violation.getLimit()) + (violation.getLimitReduction() != 1f ? " * " + violation.getLimitReduction() : "")) - .writeCell(Math.round(Math.abs(violation.getValue()) / violation.getLimit() * 100f)); + .writeCell(getViolationLimit(violation)) + .writeCell(getViolationValue(violation)); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/security-analysis/src/main/java/eu/itesla_project/security/SecurityAnalysisImpl.java b/security-analysis/src/main/java/eu/itesla_project/security/SecurityAnalysisImpl.java index 49531d98..68fdf0ac 100644 --- a/security-analysis/src/main/java/eu/itesla_project/security/SecurityAnalysisImpl.java +++ b/security-analysis/src/main/java/eu/itesla_project/security/SecurityAnalysisImpl.java @@ -41,7 +41,7 @@ public SecurityAnalysisImpl(Network network, ComputationManager computationManag } private static List checkLimits(Network network) { - return Security.checkLimits(network, Security.CurrentLimitType.TATL, 1f); + return Security.checkLimits(network, 1f); } @Override diff --git a/security-analysis/src/main/java/eu/itesla_project/security/SecurityAnalysisTool.java b/security-analysis/src/main/java/eu/itesla_project/security/SecurityAnalysisTool.java index b7ce33ca..063be18f 100644 --- a/security-analysis/src/main/java/eu/itesla_project/security/SecurityAnalysisTool.java +++ b/security-analysis/src/main/java/eu/itesla_project/security/SecurityAnalysisTool.java @@ -6,6 +6,19 @@ */ package eu.itesla_project.security; +import com.google.auto.service.AutoService; +import eu.itesla_project.commons.io.table.AsciiTableFormatterFactory; +import eu.itesla_project.commons.io.table.CsvTableFormatterFactory; +import eu.itesla_project.commons.tools.Command; +import eu.itesla_project.commons.tools.Tool; +import eu.itesla_project.commons.tools.ToolRunningContext; +import eu.itesla_project.security.SecurityAnalyzer.Format; +import eu.itesla_project.security.json.SecurityAnalysisResultSerializer; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; @@ -18,21 +31,6 @@ import java.util.Set; import java.util.stream.Collectors; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; - -import com.google.auto.service.AutoService; - -import eu.itesla_project.commons.io.table.AsciiTableFormatterFactory; -import eu.itesla_project.commons.io.table.CsvTableFormatterFactory; -import eu.itesla_project.commons.tools.Command; -import eu.itesla_project.commons.tools.Tool; -import eu.itesla_project.commons.tools.ToolRunningContext; -import eu.itesla_project.security.json.SecurityAnalysisResultSerializer; -import eu.itesla_project.security.SecurityAnalyzer.Format; - /** * @author Geoffroy Jamgotchian */ @@ -119,7 +117,8 @@ public void run(CommandLine line, ToolRunningContext context) throws Exception { if (!result.getPreContingencyResult().isComputationOk()) { context.getErrorStream().println("Pre-contingency state divergence"); } - LimitViolationFilter limitViolationFilter = new LimitViolationFilter(limitViolationTypes); + LimitViolationFilter limitViolationFilter = LimitViolationFilter.load(); + limitViolationFilter.setViolationTypes(limitViolationTypes); if (outputFile != null) { exportResult(result, limitViolationFilter, context, outputFile, format); } else { diff --git a/security-analysis/src/main/java/eu/itesla_project/security/json/LimitViolationDeserializer.java b/security-analysis/src/main/java/eu/itesla_project/security/json/LimitViolationDeserializer.java index 858e2af4..b02bc81f 100644 --- a/security-analysis/src/main/java/eu/itesla_project/security/json/LimitViolationDeserializer.java +++ b/security-analysis/src/main/java/eu/itesla_project/security/json/LimitViolationDeserializer.java @@ -29,14 +29,14 @@ class LimitViolationDeserializer extends StdDeserializer { public LimitViolation deserialize(JsonParser parser, DeserializationContext deserializationContext) throws IOException { String subjectId = null; LimitViolationType limitType = null; - float limit = Float.NaN; String limitName = null; + float limit = Float.NaN; float limitReduction = Float.NaN; float value = Float.NaN; Country country = null; float baseVoltage = Float.NaN; - while (parser.nextToken() != JsonToken.END_OBJECT) { + while (parser.nextToken() != JsonToken.END_OBJECT) { switch (parser.getCurrentName()) { case "subjectId": subjectId = parser.nextTextValue(); @@ -47,15 +47,15 @@ public LimitViolation deserialize(JsonParser parser, DeserializationContext dese limitType = parser.readValueAs(LimitViolationType.class); break; + case "limitName": + limitName = parser.nextTextValue(); + break; + case "limit": parser.nextToken(); limit = parser.readValueAs(Float.class); break; - case "limitName": - limitName = parser.nextTextValue(); - break; - case "limitReduction": parser.nextToken(); limitReduction = parser.readValueAs(Float.class); @@ -82,7 +82,5 @@ public LimitViolation deserialize(JsonParser parser, DeserializationContext dese } return new LimitViolation(subjectId, limitType, limit, limitName, limitReduction, value, country, baseVoltage); - - } } diff --git a/security-analysis/src/main/java/eu/itesla_project/security/json/LimitViolationSerializer.java b/security-analysis/src/main/java/eu/itesla_project/security/json/LimitViolationSerializer.java new file mode 100644 index 00000000..4b786c7d --- /dev/null +++ b/security-analysis/src/main/java/eu/itesla_project/security/json/LimitViolationSerializer.java @@ -0,0 +1,41 @@ +/** + * 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.security.json; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import eu.itesla_project.commons.json.JsonUtil; +import eu.itesla_project.security.LimitViolation; + +import java.io.IOException; + +/** + * @author Mathieu Bague + */ +public class LimitViolationSerializer extends StdSerializer { + + public LimitViolationSerializer() { + super(LimitViolation.class); + } + + @Override + public void serialize(LimitViolation limitViolation, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeStartObject(); + + jsonGenerator.writeStringField("subjectId", limitViolation.getSubjectId()); + jsonGenerator.writeStringField("limitType", limitViolation.getLimitType().name()); + JsonUtil.writeOptionalStringField(jsonGenerator, "limitName", limitViolation.getLimitName()); + jsonGenerator.writeNumberField("limit", limitViolation.getLimit()); + jsonGenerator.writeNumberField("limitReduction", limitViolation.getLimitReduction()); + jsonGenerator.writeNumberField("value", limitViolation.getValue()); + JsonUtil.writeOptionalEnumField(jsonGenerator, "country", limitViolation.getCountry()); + JsonUtil.writeOptionalFloatField(jsonGenerator, "baseVoltage", limitViolation.getBaseVoltage()); + + jsonGenerator.writeEndObject(); + } +} diff --git a/security-analysis/src/main/java/eu/itesla_project/security/json/SecurityAnalysisResultDeserializer.java b/security-analysis/src/main/java/eu/itesla_project/security/json/SecurityAnalysisResultDeserializer.java index 733e921d..628febce 100644 --- a/security-analysis/src/main/java/eu/itesla_project/security/json/SecurityAnalysisResultDeserializer.java +++ b/security-analysis/src/main/java/eu/itesla_project/security/json/SecurityAnalysisResultDeserializer.java @@ -6,6 +6,23 @@ */ package eu.itesla_project.security.json; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import eu.itesla_project.contingency.Contingency; +import eu.itesla_project.contingency.ContingencyElement; +import eu.itesla_project.contingency.json.ContingencyDeserializer; +import eu.itesla_project.contingency.json.ContingencyElementDeserializer; +import eu.itesla_project.security.LimitViolation; +import eu.itesla_project.security.LimitViolationsResult; +import eu.itesla_project.security.PostContingencyResult; +import eu.itesla_project.security.SecurityAnalysisResult; + import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; @@ -16,17 +33,6 @@ import java.util.List; import java.util.Objects; -import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.module.SimpleModule; - -import eu.itesla_project.contingency.*; -import eu.itesla_project.contingency.json.ContingencyDeserializer; -import eu.itesla_project.contingency.json.ContingencyElementDeserializer; -import eu.itesla_project.security.*; - /** * * @author Massimo Ferraro diff --git a/security-analysis/src/main/java/eu/itesla_project/security/json/SecurityAnalysisResultSerializer.java b/security-analysis/src/main/java/eu/itesla_project/security/json/SecurityAnalysisResultSerializer.java index b5f815a0..1d8b69fd 100644 --- a/security-analysis/src/main/java/eu/itesla_project/security/json/SecurityAnalysisResultSerializer.java +++ b/security-analysis/src/main/java/eu/itesla_project/security/json/SecurityAnalysisResultSerializer.java @@ -6,18 +6,15 @@ */ package eu.itesla_project.security.json; -import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import eu.itesla_project.security.LimitViolationFilter; -import eu.itesla_project.security.LimitViolationsResult; -import eu.itesla_project.security.PostContingencyResult; -import eu.itesla_project.security.SecurityAnalysisResult; +import eu.itesla_project.contingency.ContingencyElement; +import eu.itesla_project.contingency.json.ContingencyElementSerializer; +import eu.itesla_project.security.*; import java.io.IOException; import java.io.OutputStream; @@ -60,8 +57,7 @@ public static void write(SecurityAnalysisResult result, LimitViolationFilter fil } } - public static void write(SecurityAnalysisResult result, LimitViolationFilter filter, - OutputStream out) throws JsonGenerationException, JsonMappingException, IOException { + public static void write(SecurityAnalysisResult result, LimitViolationFilter filter, OutputStream out) throws IOException { Objects.requireNonNull(result); Objects.requireNonNull(filter); Objects.requireNonNull(out); @@ -70,6 +66,8 @@ public static void write(SecurityAnalysisResult result, LimitViolationFilter fil module.addSerializer(SecurityAnalysisResult.class, new SecurityAnalysisResultSerializer()); module.addSerializer(PostContingencyResult.class, new PostContingencyResultSerializer()); module.addSerializer(LimitViolationsResult.class, new LimitViolationsResultSerializer(filter)); + module.addSerializer(LimitViolation.class, new LimitViolationSerializer()); + module.addSerializer(ContingencyElement.class, new ContingencyElementSerializer()); objectMapper.registerModule(module); ObjectWriter writer = objectMapper.writerWithDefaultPrettyPrinter(); diff --git a/security-analysis/src/test/java/eu/itesla_project/security/SecurityTest.java b/security-analysis/src/test/java/eu/itesla_project/security/SecurityTest.java index 4712b024..61f34e22 100644 --- a/security-analysis/src/test/java/eu/itesla_project/security/SecurityTest.java +++ b/security-analysis/src/test/java/eu/itesla_project/security/SecurityTest.java @@ -6,8 +6,15 @@ */ package eu.itesla_project.security; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import eu.itesla_project.commons.io.table.CsvTableFormatterFactory; +import eu.itesla_project.commons.io.table.TableFormatterConfig; +import eu.itesla_project.contingency.Contingency; +import eu.itesla_project.iidm.network.Bus; +import eu.itesla_project.iidm.network.Network; +import eu.itesla_project.iidm.network.test.EurostagTutorialExample1Factory; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; import java.io.StringWriter; import java.util.Arrays; @@ -15,17 +22,8 @@ import java.util.List; import java.util.Locale; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import eu.itesla_project.commons.io.table.CsvTableFormatterFactory; -import eu.itesla_project.commons.io.table.TableFormatterConfig; -import eu.itesla_project.contingency.Contingency; -import eu.itesla_project.iidm.network.Bus; -import eu.itesla_project.iidm.network.Network; -import eu.itesla_project.iidm.network.test.EurostagTutorialExample1Factory; -import eu.itesla_project.security.Security.CurrentLimitType; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * @author Geoffroy Jamgotchian @@ -64,7 +62,7 @@ public void printPreContingencyViolations() throws Exception { } assertEquals(String.join(System.lineSeparator(), "Pre-contingency violations", - "Action,Equipment,Violation type,Violation name,Value,Limit,Charge %", + "Action,Equipment,Violation type,Violation name,Value,Limit,Loading rate %", "action1,,,,,,", ",line1,CURRENT,20',1100.00,1000.0,110"), writer.toString().trim()); @@ -80,7 +78,7 @@ public void printPostContingencyViolations() throws Exception { } assertEquals(String.join(System.lineSeparator(), "Post-contingency limit violations", - "Contingency,Status,Action,Equipment,Violation type,Violation name,Value,Limit,Charge %", + "Contingency,Status,Action,Equipment,Violation type,Violation name,Value,Limit,Loading rate %", "contingency1,converge,,,,,,,", ",,action2,,,,,,", ",,,line1,CURRENT,20',1100.00,1000.0,110", @@ -98,7 +96,7 @@ public void printPostContingencyViolationsWithPreContingencyViolationsFiltering( } assertEquals(String.join(System.lineSeparator(), "Post-contingency limit violations", - "Contingency,Status,Action,Equipment,Violation type,Violation name,Value,Limit,Charge %", + "Contingency,Status,Action,Equipment,Violation type,Violation name,Value,Limit,Loading rate %", "contingency1,converge,,,,,,,", ",,action2,,,,,,", ",,,line2,CURRENT,10',950.000,900.0,106"), @@ -108,12 +106,12 @@ public void printPostContingencyViolationsWithPreContingencyViolationsFiltering( @Test public void printLimitsViolations() { assertEquals(String.join("\n", - "+---------+--------------+---------------+----------------+----------------+--------+--------+------------------+----------+", - "| Country | Base voltage | Equipment (2) | Violation type | Violation name | value | limit | abs(value-limit) | charge % |", - "+---------+--------------+---------------+----------------+----------------+--------+--------+------------------+----------+", - "| | | line1 | CURRENT | 20' | 1100.0 | 1000.0 | 100.0 | 110 |", - "| | | line2 | CURRENT | 10' | 950.0 | 900.0 | 50.0 | 106 |", - "+---------+--------------+---------------+----------------+----------------+--------+--------+------------------+----------+"), + "+---------+--------------+---------------+----------------+----------------+--------+--------+------------------+----------------+", + "| Country | Base voltage | Equipment (2) | Violation type | Violation name | Value | Limit | abs(value-limit) | Loading rate % |", + "+---------+--------------+---------------+----------------+----------------+--------+--------+------------------+----------------+", + "| | | line1 | CURRENT | 20' | 1100.0 | 1000.0 | 100.0 | 110 |", + "| | | line2 | CURRENT | 10' | 950.0 | 900.0 | 50.0 | 106 |", + "+---------+--------------+---------------+----------------+----------------+--------+--------+------------------+----------------+"), Security.printLimitsViolations(Arrays.asList(line1Violation, line2Violation), new LimitViolationFilter())); } @@ -124,7 +122,7 @@ public void checkLimits() { ((Bus) network.getIdentifiable("NHV2")).setV(380f).getVoltageLevel().setLowVoltageLimit(300f).setHighVoltageLimit(500f); 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").newCurrentLimits1().setPermanentLimit(500f).add(); network.getLine("NHV1_NHV2_1").newCurrentLimits2() .setPermanentLimit(1100f) .beginTemporaryLimit() @@ -143,11 +141,11 @@ public void checkLimits() { .setValue(1200) .endTemporaryLimit() .add(); - network.getLine("NHV1_NHV2_2").newCurrentLimits2().setPermanentLimit(1500f).add(); + network.getLine("NHV1_NHV2_2").newCurrentLimits2().setPermanentLimit(500f).add(); List violations = Security.checkLimits(network); - assertEquals(3, violations.size()); + assertEquals(5, violations.size()); violations.forEach(violation -> { assertTrue(Arrays.asList("VLHV1", "NHV1_NHV2_1", "NHV1_NHV2_2").contains(violation.getSubjectId())); if ("VLHV1".equals(violation.getSubjectId())) { @@ -157,9 +155,9 @@ public void checkLimits() { } }); - violations = Security.checkLimits(network, CurrentLimitType.TATL, 1); + violations = Security.checkLimits(network, 1); - assertEquals(3, violations.size()); + assertEquals(5, violations.size()); violations.forEach(violation -> { assertTrue(Arrays.asList("VLHV1", "NHV1_NHV2_1", "NHV1_NHV2_2").contains(violation.getSubjectId())); if ("VLHV1".equals(violation.getSubjectId())) { diff --git a/security-analysis/src/test/java/eu/itesla_project/security/json/SecurityAnalysisResultJsonTest.java b/security-analysis/src/test/java/eu/itesla_project/security/json/SecurityAnalysisResultJsonTest.java index b695d2c6..3caa2def 100644 --- a/security-analysis/src/test/java/eu/itesla_project/security/json/SecurityAnalysisResultJsonTest.java +++ b/security-analysis/src/test/java/eu/itesla_project/security/json/SecurityAnalysisResultJsonTest.java @@ -23,8 +23,11 @@ public class SecurityAnalysisResultJsonTest extends AbstractConverterTest { private static SecurityAnalysisResult create() { - LimitViolation violation1 = new LimitViolation("NHV1_NHV2_1", LimitViolationType.CURRENT, 100f, "limit", .95f, 110f, Country.FR, 380f); - LimitViolation violation2 = new LimitViolation("NHV1_NHV2_2", LimitViolationType.CURRENT, 100f, null, 110f); + // Create a LimitViolation(CURRENT) to ensure backward compatibility works + LimitViolation violation1 = new LimitViolation("NHV1_NHV2_1", LimitViolationType.CURRENT, 100f, "limit", 0.95f, 110f, null, Float.NaN); + LimitViolation violation2 = new LimitViolation("NHV1_NHV2_2", LimitViolationType.CURRENT, 100f, "20'", 110f); + LimitViolation violation3 = new LimitViolation("GEN", LimitViolationType.HIGH_VOLTAGE, 100f, null, 0.9f, 110f, null, Float.NaN); + LimitViolation violation4 = new LimitViolation("GEN2", LimitViolationType.LOW_VOLTAGE, 100f, null, 0.7f, 115f, Country.FR, 400.0f); List elements = Arrays.asList( new BranchContingency("NHV1_NHV2_2", "VLNHV1"), @@ -35,7 +38,7 @@ private static SecurityAnalysisResult create() { Contingency contingency = new ContingencyImpl("contingency", elements); LimitViolationsResult preContingencyResult = new LimitViolationsResult(true, Collections.singletonList(violation1)); - PostContingencyResult postContingencyResult = new PostContingencyResult(contingency, true, Arrays.asList(violation1, violation2), Arrays.asList("action1", "action2")); + PostContingencyResult postContingencyResult = new PostContingencyResult(contingency, true, Arrays.asList(violation2, violation3, violation4), Arrays.asList("action1", "action2")); return new SecurityAnalysisResult(preContingencyResult, Collections.singletonList(postContingencyResult)); } diff --git a/security-analysis/src/test/resources/SecurityAnalysisResult.json b/security-analysis/src/test/resources/SecurityAnalysisResult.json index 6c256286..fb1e7aca 100644 --- a/security-analysis/src/test/resources/SecurityAnalysisResult.json +++ b/security-analysis/src/test/resources/SecurityAnalysisResult.json @@ -5,12 +5,10 @@ "limitViolations" : [ { "subjectId" : "NHV1_NHV2_1", "limitType" : "CURRENT", - "limit" : 100.0, "limitName" : "limit", + "limit" : 100.0, "limitReduction" : 0.95, - "value" : 110.0, - "country" : "FR", - "baseVoltage" : 380.0 + "value" : 110.0 } ], "actionsTaken" : [ ] }, @@ -19,11 +17,10 @@ "id" : "contingency", "elements" : [ { "id" : "NHV1_NHV2_2", - "voltageLevelId" : "VLNHV1", - "type" : "BRANCH" + "type" : "BRANCH", + "voltageLevelId" : "VLNHV1" }, { "id" : "NHV1_NHV2_1", - "voltageLevelId" : null, "type" : "BRANCH" }, { "id" : "GEN", @@ -36,23 +33,26 @@ "limitViolationsResult" : { "computationOk" : true, "limitViolations" : [ { - "subjectId" : "NHV1_NHV2_1", - "limitType" : "CURRENT", - "limit" : 100.0, - "limitName" : "limit", - "limitReduction" : 0.95, - "value" : 110.0, - "country" : "FR", - "baseVoltage" : 380.0 - }, { "subjectId" : "NHV1_NHV2_2", "limitType" : "CURRENT", + "limitName" : "20'", "limit" : 100.0, - "limitName" : null, "limitReduction" : 1.0, - "value" : 110.0, - "country" : null, - "baseVoltage" : "NaN" + "value" : 110.0 + }, { + "subjectId" : "GEN", + "limitType" : "HIGH_VOLTAGE", + "limit" : 100.0, + "limitReduction" : 0.9, + "value" : 110.0 + }, { + "subjectId" : "GEN2", + "limitType" : "LOW_VOLTAGE", + "limit" : 100.0, + "limitReduction" : 0.7, + "value" : 115.0, + "country" : "FR", + "baseVoltage" : 400.0 } ], "actionsTaken" : [ "action1", "action2" ] }