From 822f152bb4ffb84e35daa5bc1d617e8253a39af1 Mon Sep 17 00:00:00 2001 From: Philippe Edwards Date: Tue, 11 Mar 2025 09:46:24 +0100 Subject: [PATCH 1/4] created ICS importer --- ra-optimisation/rao-api/pom.xml | 5 + .../powsybl/openrao/raoapi/IcsImporter.java | 140 ++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java diff --git a/ra-optimisation/rao-api/pom.xml b/ra-optimisation/rao-api/pom.xml index 2bc9d9cb8d..52a715214a 100644 --- a/ra-optimisation/rao-api/pom.xml +++ b/ra-optimisation/rao-api/pom.xml @@ -61,6 +61,11 @@ org.apache.commons commons-lang3 + + org.apache.commons + commons-csv + 1.13.0 + diff --git a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java new file mode 100644 index 0000000000..3752719b9f --- /dev/null +++ b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java @@ -0,0 +1,140 @@ +package com.powsybl.openrao.raoapi; + +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.LoadType; +import com.powsybl.openrao.data.crac.api.Crac; +import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeActionAdder; +import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; +import com.powsybl.openrao.data.crac.api.usagerule.UsageMethod; +import com.powsybl.openrao.data.intertemporalconstraint.PowerGradient; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVRecord; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.time.OffsetDateTime; +import java.util.HashMap; +import java.util.Map; + +public final class IcsImporter { + private static final int OFFSET = 2; + private static final double COST_UP = 10; + private static final double COST_DOWN = 10; + + //TODO:QUALITY CHECK: do PO respect constraints? + + private IcsImporter() { + //should only be used statically + } + + public static void populateInputWithICS(InterTemporalRaoInput interTemporalRaoInput, InputStream staticInputStream, InputStream seriesInputStream) throws IOException { + CSVFormat csvFormat = CSVFormat.DEFAULT.builder() + .setDelimiter(";") + .setHeader() + .setSkipHeaderRecord(true) + .get(); + Iterable staticCsvRecords = csvFormat.parse(new InputStreamReader(staticInputStream)); + Iterable seriesCsvRecords = csvFormat.parse(new InputStreamReader(seriesInputStream)); + + Map> seriesPerIdAndType = new HashMap<>(); + seriesCsvRecords.forEach(record -> { + seriesPerIdAndType.putIfAbsent(record.get("RA RD ID"), new HashMap<>()); + seriesPerIdAndType.get(record.get("RA RD ID")).put(record.get("Type of timeseries"), record); + }); + + staticCsvRecords.forEach(staticRecord -> { + if (shouldBeImported(staticRecord)) { + String raId = staticRecord.get("RA RD ID"); + Map seriesPerType = seriesPerIdAndType.get(raId); + if (seriesPerType != null && seriesPerType.containsKey("P0") && seriesPerType.containsKey("RDP-") && seriesPerType.containsKey("RDP+")) { + String networkElement = processNetworks(staticRecord.get("UCT Node or GSK ID"), interTemporalRaoInput, seriesPerType); + if (networkElement == null) { + return; + } + interTemporalRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { + Crac crac = raoInput.getCrac(); + Double p0 = Double.parseDouble(seriesPerType.get("P0").get(dateTime.getHour() + OFFSET)); + InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() + .withId(raId + "_RD") + .withName(staticRecord.get("Generator Name")) + .withNetworkElement(networkElement) + .withInitialSetpoint(p0) + .withVariationCost(COST_UP, VariationDirection.UP) + .withVariationCost(COST_DOWN, VariationDirection.DOWN) + .newRange() + .withMin(p0 - Double.parseDouble(seriesPerType.get("RDP-").get(dateTime.getHour() + OFFSET))) + .withMax(p0 + Double.parseDouble(seriesPerType.get("RDP+").get(dateTime.getHour() + OFFSET))) + .add(); + if (staticRecord.get("Preventive").equals("TRUE")) { + injectionRangeActionAdder.newOnInstantUsageRule() + .withInstant(crac.getPreventiveInstant().getId()) + .withUsageMethod(UsageMethod.AVAILABLE) + .add(); + } + if (staticRecord.get("Curative").equals("TRUE")) { + injectionRangeActionAdder.newOnInstantUsageRule() + .withInstant(crac.getLastInstant().getId()) + .withUsageMethod(UsageMethod.AVAILABLE) + .add(); + } + injectionRangeActionAdder.add(); + + }); + + PowerGradient powerGradient = PowerGradient.builder() + .withNetworkElementId(networkElement) + .withMaxValue(Double.parseDouble( + staticRecord.get("Maximum positive power gradient [MW/h]").isEmpty() ? "1000" : staticRecord.get("Maximum positive power gradient [MW/h]") + )).withMinValue(-Double.parseDouble( + staticRecord.get("Maximum negative power gradient [MW/h]").isEmpty() ? "1000" : staticRecord.get("Maximum negative power gradient [MW/h]") + )).build(); + interTemporalRaoInput.getPowerGradients().add(powerGradient); + } + } + }); + + } + + private static String processNetworks(String uctNodeOrGskId, InterTemporalRaoInput interTemporalRaoInput, Map seriesPerType) { + String generatorId = seriesPerType.get("P0").get("RA RD ID") + "_GENERATOR"; + for (Map.Entry entry : interTemporalRaoInput.getRaoInputs().getDataPerTimestamp().entrySet()) { + Bus bus = entry.getValue().getNetwork().getBusBreakerView().getBus(uctNodeOrGskId); + if (bus == null) { + return null; + } + Double p0 = Double.parseDouble(seriesPerType.get("P0").get(entry.getKey().getHour() + OFFSET)); + processBus(bus, generatorId, p0); + } + return generatorId; + } + + private static void processBus(Bus bus, String generatorId, Double p0) { + bus.getVoltageLevel().newGenerator() + .setBus(bus.getId()) + .setEnsureIdUnicity(true) + .setId(generatorId) + .setMaxP(999999) + .setMinP(0) + .setTargetP(p0) + .setTargetQ(0) + .setTargetV(bus.getVoltageLevel().getNominalV()) + .setVoltageRegulatorOn(false) + .add() + .setFictitious(true); + + bus.getVoltageLevel().newLoad() + .setBus(bus.getId()) + .setEnsureIdUnicity(true) + .setId(bus.getId() + "_LOAD") + .setP0(p0) + .setQ0(0) + .setLoadType(LoadType.FICTITIOUS) + .add(); + } + + private static boolean shouldBeImported(CSVRecord staticRecord) { + return staticRecord.get("RD description mode").equals("NODE") && + (staticRecord.get("Preventive").equals("TRUE") || staticRecord.get("Curative").equals("TRUE")); + } +} \ No newline at end of file From 12d36ef6283de0c2bf415d9eadf5e4f985bc601d Mon Sep 17 00:00:00 2001 From: Philippe Edwards Date: Tue, 11 Mar 2025 09:46:36 +0100 Subject: [PATCH 2/4] Revert "remove power gradients" This reverts commit 4303744c3b8319e286d546e113e63ffd2bcf09c8. --- data/inter-temporal-constraint/pom.xml | 34 +++++++ .../PowerGradient.java | 89 +++++++++++++++++++ .../PowerGradientTest.java | 80 +++++++++++++++++ data/pom.xml | 1 + distribution/pom.xml | 5 ++ pom.xml | 5 ++ ra-optimisation/rao-api/pom.xml | 9 +- .../openrao/raoapi/InterTemporalRaoInput.java | 16 ++-- .../raoapi/InterTemporalRaoInputTest.java | 17 ++-- .../PowerGradientConstraintFiller.java | 22 ++--- ...InterTemporalIteratingLinearOptimizer.java | 2 +- ...TemporalIteratingLinearOptimizerInput.java | 4 +- .../openrao/searchtreerao/marmot/Marmot.java | 4 +- .../PowerGradientConstraintFillerTest.java | 32 +++---- .../searchtreerao/marmot/MarmotTest.java | 10 +-- 15 files changed, 272 insertions(+), 58 deletions(-) create mode 100644 data/inter-temporal-constraint/pom.xml create mode 100644 data/inter-temporal-constraint/src/main/java/com/powsybl/openrao/data/intertemporalconstraint/PowerGradient.java create mode 100644 data/inter-temporal-constraint/src/test/java/com/powsybl/openrao/data/intertemporalconstraint/PowerGradientTest.java diff --git a/data/inter-temporal-constraint/pom.xml b/data/inter-temporal-constraint/pom.xml new file mode 100644 index 0000000000..e7e997f50b --- /dev/null +++ b/data/inter-temporal-constraint/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + + open-rao-data + com.powsybl + 6.4.0-SNAPSHOT + + + open-rao-inter-temporal-constraint + jar + Inter-temporal Constraints + Module for inter-temporal constraints + + + + + ${project.groupId} + open-rao-crac-api + ${project.version} + + + + + org.junit.jupiter + junit-jupiter + test + + + + \ No newline at end of file diff --git a/data/inter-temporal-constraint/src/main/java/com/powsybl/openrao/data/intertemporalconstraint/PowerGradient.java b/data/inter-temporal-constraint/src/main/java/com/powsybl/openrao/data/intertemporalconstraint/PowerGradient.java new file mode 100644 index 0000000000..7ff43db238 --- /dev/null +++ b/data/inter-temporal-constraint/src/main/java/com/powsybl/openrao/data/intertemporalconstraint/PowerGradient.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package com.powsybl.openrao.data.intertemporalconstraint; + +import com.powsybl.openrao.commons.OpenRaoException; + +import java.util.Optional; + +// TODO: delete this class and this module and replace by GeneratorConstraints + +/** + * Power Gradient (in MW/hour) that applies on a generator or a load. + * It has a negative minimum value and/or a positive maximum value. + * + * @author Thomas Bouquet {@literal } + */ +@Deprecated +public final class PowerGradient { + private final String networkElementId; + private final Double minValue; + private final Double maxValue; + + public PowerGradient(String networkElementId, Double minValue, Double maxValue) { + this.networkElementId = networkElementId; + this.minValue = minValue; + this.maxValue = maxValue; + } + + public String getNetworkElementId() { + return networkElementId; + } + + public Optional getMinValue() { + return Optional.ofNullable(minValue); + } + + public Optional getMaxValue() { + return Optional.ofNullable(maxValue); + } + + public static PowerGradientBuilder builder() { + return new PowerGradientBuilder(); + } + + public static final class PowerGradientBuilder { + private String networkElementId; + private Double minValue; + private Double maxValue; + + private PowerGradientBuilder() { + } + + public PowerGradientBuilder withNetworkElementId(String networkElementId) { + this.networkElementId = networkElementId; + return this; + } + + public PowerGradientBuilder withMinValue(Double minPowerGradient) { + this.minValue = minPowerGradient; + return this; + } + + public PowerGradientBuilder withMaxValue(Double maxPowerGradient) { + this.maxValue = maxPowerGradient; + return this; + } + + public PowerGradient build() { + if (networkElementId == null) { + throw new OpenRaoException("No network element id provided."); + } + if (minValue == null && maxValue == null) { + throw new OpenRaoException("At least one of min or max power gradient's value must be provided."); + } + if (minValue != null && minValue > 0) { + throw new OpenRaoException("The min value of the power gradient must be negative."); + } + if (maxValue != null && maxValue < 0) { + throw new OpenRaoException("The max value of the power gradient must be positive."); + } + return new PowerGradient(networkElementId, minValue, maxValue); + } + } +} diff --git a/data/inter-temporal-constraint/src/test/java/com/powsybl/openrao/data/intertemporalconstraint/PowerGradientTest.java b/data/inter-temporal-constraint/src/test/java/com/powsybl/openrao/data/intertemporalconstraint/PowerGradientTest.java new file mode 100644 index 0000000000..ac69716849 --- /dev/null +++ b/data/inter-temporal-constraint/src/test/java/com/powsybl/openrao/data/intertemporalconstraint/PowerGradientTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package com.powsybl.openrao.data.intertemporalconstraint; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.powsybl.openrao.commons.OpenRaoException; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +/** + * @author Thomas Bouquet {@literal } + */ +class PowerGradientTest { + @Test + void buildExhaustivePowerGradientConstraint() { + PowerGradient powerGradient = PowerGradient.builder() + .withNetworkElementId("generator") + .withMinValue(-100.0) + .withMaxValue(100.0) + .build(); + assertEquals("generator", powerGradient.getNetworkElementId()); + assertEquals(Optional.of(-100.0), powerGradient.getMinValue()); + assertEquals(Optional.of(100.0), powerGradient.getMaxValue()); + } + + @Test + void buildPowerGradientConstraintWithoutMin() { + PowerGradient powerGradient = PowerGradient.builder() + .withNetworkElementId("generator") + .withMaxValue(100.0) + .build(); + assertEquals("generator", powerGradient.getNetworkElementId()); + assertTrue(powerGradient.getMinValue().isEmpty()); + assertEquals(Optional.of(100.0), powerGradient.getMaxValue()); + } + + @Test + void buildPowerGradientConstraintWithoutMax() { + PowerGradient powerGradient = PowerGradient.builder() + .withNetworkElementId("generator") + .withMinValue(-100.0) + .build(); + assertEquals("generator", powerGradient.getNetworkElementId()); + assertEquals(Optional.of(-100.0), powerGradient.getMinValue()); + assertTrue(powerGradient.getMaxValue().isEmpty()); + } + + @Test + void buildPowerGradientConstraintWithoutNetworkElement() { + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> PowerGradient.builder().withMinValue(-100.0).withMaxValue(100.0).build()); + assertEquals("No network element id provided.", exception.getMessage()); + } + + @Test + void buildPowerGradientConstraintWithoutMinAndMax() { + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> PowerGradient.builder().withNetworkElementId("generator").build()); + assertEquals("At least one of min or max power gradient's value must be provided.", exception.getMessage()); + } + + @Test + void buildPowerGradientConstraintPositiveMin() { + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> PowerGradient.builder().withNetworkElementId("generator").withMinValue(100.0).build()); + assertEquals("The min value of the power gradient must be negative.", exception.getMessage()); + } + + @Test + void buildPowerGradientConstraintNegativeMax() { + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> PowerGradient.builder().withNetworkElementId("generator").withMaxValue(-100.0).build()); + assertEquals("The max value of the power gradient must be positive.", exception.getMessage()); + } +} diff --git a/data/pom.xml b/data/pom.xml index 068e4bc780..491bf4ddf3 100755 --- a/data/pom.xml +++ b/data/pom.xml @@ -20,6 +20,7 @@ flowbased-domain generator-constraints glsk + inter-temporal-constraint rao-result refprog virtual-hubs diff --git a/distribution/pom.xml b/distribution/pom.xml index d97a2fed94..df79465edc 100755 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -87,6 +87,11 @@ open-rao-generator-constraints ${project.version} + + ${project.groupId} + open-rao-inter-temporal-constraint + ${project.version} + ${project.groupId} open-rao-loopflow-computation diff --git a/pom.xml b/pom.xml index a5662923d3..e1b75eff7a 100644 --- a/pom.xml +++ b/pom.xml @@ -295,6 +295,11 @@ open-rao-glsk-virtual-hubs ${project.version} + + ${project.groupId} + open-rao-inter-temporal-constraint + ${project.version} + ${project.groupId} open-rao-rao-result diff --git a/ra-optimisation/rao-api/pom.xml b/ra-optimisation/rao-api/pom.xml index 52a715214a..525b5245e5 100644 --- a/ra-optimisation/rao-api/pom.xml +++ b/ra-optimisation/rao-api/pom.xml @@ -22,6 +22,11 @@ open-rao-crac-api ${project.version} + + com.powsybl + open-rao-inter-temporal-constraint + ${project.version} + ${project.groupId} open-rao-rao-result-api @@ -115,9 +120,5 @@ mockito-core test - - com.powsybl - open-rao-generator-constraints - \ No newline at end of file diff --git a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/InterTemporalRaoInput.java b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/InterTemporalRaoInput.java index 352712ad16..8c2e8d0e11 100644 --- a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/InterTemporalRaoInput.java +++ b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/InterTemporalRaoInput.java @@ -9,7 +9,7 @@ import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.TemporalData; -import com.powsybl.openrao.data.generatorconstraints.GeneratorConstraints; +import com.powsybl.openrao.data.intertemporalconstraint.PowerGradient; import java.time.OffsetDateTime; import java.util.HashSet; @@ -23,17 +23,17 @@ public class InterTemporalRaoInput { private final TemporalData raoInputs; private final Set timestampsToRun; - private final Set generatorConstraints; + private final Set powerGradients; - public InterTemporalRaoInput(TemporalData raoInputs, Set timestampsToRun, Set powerGradients) { + public InterTemporalRaoInput(TemporalData raoInputs, Set timestampsToRun, Set powerGradients) { this.raoInputs = raoInputs; this.timestampsToRun = timestampsToRun; - this.generatorConstraints = powerGradients; + this.powerGradients = powerGradients; checkTimestampsToRun(); } - public InterTemporalRaoInput(TemporalData raoInputs, Set generatorConstraints) { - this(raoInputs, new HashSet<>(raoInputs.getTimestamps()), generatorConstraints); + public InterTemporalRaoInput(TemporalData raoInputs, Set powerGradients) { + this(raoInputs, new HashSet<>(raoInputs.getTimestamps()), powerGradients); } public TemporalData getRaoInputs() { @@ -44,8 +44,8 @@ public Set getTimestampsToRun() { return timestampsToRun; } - public Set getGeneratorConstraints() { - return generatorConstraints; + public Set getPowerGradients() { + return powerGradients; } private void checkTimestampsToRun() { diff --git a/ra-optimisation/rao-api/src/test/java/com/powsybl/openrao/raoapi/InterTemporalRaoInputTest.java b/ra-optimisation/rao-api/src/test/java/com/powsybl/openrao/raoapi/InterTemporalRaoInputTest.java index 9a59ca45cb..5a4dba1717 100644 --- a/ra-optimisation/rao-api/src/test/java/com/powsybl/openrao/raoapi/InterTemporalRaoInputTest.java +++ b/ra-optimisation/rao-api/src/test/java/com/powsybl/openrao/raoapi/InterTemporalRaoInputTest.java @@ -10,7 +10,7 @@ import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; -import com.powsybl.openrao.data.generatorconstraints.GeneratorConstraints; +import com.powsybl.openrao.data.intertemporalconstraint.PowerGradient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -32,7 +32,7 @@ class InterTemporalRaoInputTest { private OffsetDateTime timestamp2; private OffsetDateTime timestamp3; private TemporalData temporalData; - private Set generatorConstraints; + private Set powerGradients; @BeforeEach void setUp() { @@ -43,26 +43,23 @@ void setUp() { timestamp2 = OffsetDateTime.of(2024, 12, 10, 17, 21, 0, 0, ZoneOffset.UTC); timestamp3 = OffsetDateTime.of(2024, 12, 10, 18, 21, 0, 0, ZoneOffset.UTC); temporalData = new TemporalDataImpl<>(Map.of(timestamp1, raoInput1, timestamp2, raoInput2, timestamp3, raoInput3)); - generatorConstraints = Set.of( - GeneratorConstraints.create().withGeneratorId("generator-1").withLeadTime(0.0).withLagTime(0.0).withPMin(400.0).withPMax(1000.0).withUpwardPowerGradient(200.0).build(), - GeneratorConstraints.create().withGeneratorId("generator-2").withLeadTime(0.0).withLagTime(0.0).withPMin(400.0).withPMax(1000.0).withDownwardPowerGradient(-50.0).build() - ); + powerGradients = Set.of(PowerGradient.builder().withNetworkElementId("generator-1").withMaxValue(200.0).build(), PowerGradient.builder().withNetworkElementId("generator-2").withMinValue(-50.0).build()); } @Test void testInstantiateInterTemporalRaoInput() { - InterTemporalRaoInput input = new InterTemporalRaoInput(temporalData, Set.of(timestamp1, timestamp3), generatorConstraints); + InterTemporalRaoInput input = new InterTemporalRaoInput(temporalData, Set.of(timestamp1, timestamp3), powerGradients); assertEquals(temporalData, input.getRaoInputs()); assertEquals(Set.of(timestamp1, timestamp3), input.getTimestampsToRun()); - assertEquals(generatorConstraints, input.getGeneratorConstraints()); + assertEquals(powerGradients, input.getPowerGradients()); } @Test void testInstantiateInterTemporalRaoInputAllTimestamps() { - InterTemporalRaoInput input = new InterTemporalRaoInput(temporalData, generatorConstraints); + InterTemporalRaoInput input = new InterTemporalRaoInput(temporalData, powerGradients); assertEquals(temporalData, input.getRaoInputs()); assertEquals(Set.of(timestamp1, timestamp2, timestamp3), input.getTimestampsToRun()); - assertEquals(generatorConstraints, input.getGeneratorConstraints()); + assertEquals(powerGradients, input.getPowerGradients()); } @Test diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/PowerGradientConstraintFiller.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/PowerGradientConstraintFiller.java index 0738b62e50..bc5c2a81f3 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/PowerGradientConstraintFiller.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/PowerGradientConstraintFiller.java @@ -16,7 +16,7 @@ import com.powsybl.openrao.data.crac.api.NetworkElement; import com.powsybl.openrao.data.crac.api.State; import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeAction; -import com.powsybl.openrao.data.generatorconstraints.GeneratorConstraints; +import com.powsybl.openrao.data.intertemporalconstraint.PowerGradient; import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.LinearProblem; import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.OpenRaoMPConstraint; import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.OpenRaoMPVariable; @@ -39,13 +39,13 @@ public class PowerGradientConstraintFiller implements ProblemFiller { private final TemporalData preventiveStates; private final TemporalData networkPerTimestamp; private final TemporalData> injectionRangeActionsPerTimestamp; - private final Set generatorConstraints; + private final Set powerGradients; - public PowerGradientConstraintFiller(TemporalData preventiveStates, TemporalData networkPerTimestamp, TemporalData> injectionRangeActionsPerTimestamp, Set generatorConstraints) { + public PowerGradientConstraintFiller(TemporalData preventiveStates, TemporalData networkPerTimestamp, TemporalData> injectionRangeActionsPerTimestamp, Set powerGradients) { this.preventiveStates = preventiveStates; this.networkPerTimestamp = networkPerTimestamp; this.injectionRangeActionsPerTimestamp = injectionRangeActionsPerTimestamp; - this.generatorConstraints = generatorConstraints; + this.powerGradients = powerGradients; } // TODO : only create generator variables when necessary (map injection range actions/generators) @@ -54,8 +54,8 @@ public void fill(LinearProblem linearProblem, FlowResult flowResult, Sensitivity List timestamps = preventiveStates.getTimestamps(); IntStream.range(0, timestamps.size()).forEach(timestampIndex -> { OffsetDateTime timestamp = timestamps.get(timestampIndex); - generatorConstraints.forEach(powerGradient -> { - String generatorId = powerGradient.getGeneratorId(); + powerGradients.forEach(powerGradient -> { + String generatorId = powerGradient.getNetworkElementId(); OpenRaoMPVariable generatorPowerVariable = linearProblem.addGeneratorPowerVariable(generatorId, timestamp); addPowerConstraint(linearProblem, generatorId, generatorPowerVariable, timestamp); if (timestampIndex > 0) { @@ -68,13 +68,13 @@ public void fill(LinearProblem linearProblem, FlowResult flowResult, Sensitivity /** Build a Generator Power Gradient Constraint for a generator g at timestamp t * p^{-}(g) * delta(t, t + 1) <= P(g, t + 1) - P(g, t) <= p^{+}(g) * delta_t(t, t + 1) * */ - private static void addPowerGradientConstraint(LinearProblem linearProblem, GeneratorConstraints generatorConstraints, OffsetDateTime currentTimestamp, OffsetDateTime previousTimestamp, OpenRaoMPVariable generatorPowerVariable) { + private static void addPowerGradientConstraint(LinearProblem linearProblem, PowerGradient constraint, OffsetDateTime currentTimestamp, OffsetDateTime previousTimestamp, OpenRaoMPVariable generatorPowerVariable) { double timeGap = previousTimestamp.until(currentTimestamp, ChronoUnit.HOURS); - double lb = generatorConstraints.getDownwardPowerGradient().map(minValue -> minValue * timeGap).orElse(-linearProblem.infinity()); - double ub = generatorConstraints.getUpwardPowerGradient().map(maxValue -> maxValue * timeGap).orElse(linearProblem.infinity()); - String generatorId = generatorConstraints.getGeneratorId(); + double lb = constraint.getMinValue().map(minValue -> minValue * timeGap).orElse(-linearProblem.infinity()); + double ub = constraint.getMaxValue().map(maxValue -> maxValue * timeGap).orElse(linearProblem.infinity()); + String generatorId = constraint.getNetworkElementId(); OpenRaoMPConstraint generatorPowerGradientConstraint = linearProblem.addGeneratorPowerGradientConstraint(generatorId, currentTimestamp, previousTimestamp, lb, ub); - OpenRaoMPVariable previousGeneratorPowerVariable = linearProblem.getGeneratorPowerVariable(generatorConstraints.getGeneratorId(), previousTimestamp); + OpenRaoMPVariable previousGeneratorPowerVariable = linearProblem.getGeneratorPowerVariable(constraint.getNetworkElementId(), previousTimestamp); generatorPowerGradientConstraint.setCoefficient(generatorPowerVariable, 1.0); generatorPowerGradientConstraint.setCoefficient(previousGeneratorPowerVariable, -1.0); } diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/InterTemporalIteratingLinearOptimizer.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/InterTemporalIteratingLinearOptimizer.java index 5ed43985a7..538b7f7238 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/InterTemporalIteratingLinearOptimizer.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/InterTemporalIteratingLinearOptimizer.java @@ -169,7 +169,7 @@ private static List getInterTemporalProblemFillers(InterTemporalI TemporalData preventiveStates = input.iteratingLinearOptimizerInputs().map(linearOptimizerInput -> linearOptimizerInput.optimizationPerimeter().getMainOptimizationState()); TemporalData networks = input.iteratingLinearOptimizerInputs().map(IteratingLinearOptimizerInput::network); TemporalData> preventiveInjectionRangeActions = input.iteratingLinearOptimizerInputs().map(linearOptimizerInput -> filterPreventiveInjectionRangeAction(linearOptimizerInput.optimizationPerimeter().getRangeActions())); - return List.of(new PowerGradientConstraintFiller(preventiveStates, networks, preventiveInjectionRangeActions, input.generatorConstraints())); + return List.of(new PowerGradientConstraintFiller(preventiveStates, networks, preventiveInjectionRangeActions, input.powerGradients())); } private static Set filterPreventiveInjectionRangeAction(Set> rangeActions) { diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/InterTemporalIteratingLinearOptimizerInput.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/InterTemporalIteratingLinearOptimizerInput.java index bd8a6794e4..51bdd48da7 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/InterTemporalIteratingLinearOptimizerInput.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/InterTemporalIteratingLinearOptimizerInput.java @@ -8,7 +8,7 @@ package com.powsybl.openrao.searchtreerao.marmot; import com.powsybl.openrao.commons.TemporalData; -import com.powsybl.openrao.data.generatorconstraints.GeneratorConstraints; +import com.powsybl.openrao.data.intertemporalconstraint.PowerGradient; import com.powsybl.openrao.searchtreerao.commons.objectivefunction.ObjectiveFunction; import com.powsybl.openrao.searchtreerao.linearoptimisation.inputs.IteratingLinearOptimizerInput; @@ -18,5 +18,5 @@ * @author Thomas Bouquet {@literal } * @author Godelaine de Montmorillon {@literal } */ -public record InterTemporalIteratingLinearOptimizerInput(TemporalData iteratingLinearOptimizerInputs, ObjectiveFunction objectiveFunction, Set generatorConstraints) { +public record InterTemporalIteratingLinearOptimizerInput(TemporalData iteratingLinearOptimizerInputs, ObjectiveFunction objectiveFunction, Set powerGradients) { } diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/Marmot.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/Marmot.java index 622b0e4fdf..6b47118d6d 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/Marmot.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/Marmot.java @@ -65,7 +65,7 @@ public CompletableFuture> run(InterTemporalRaoInput raoI TemporalData topologicalOptimizationResults = runTopologicalOptimization(raoInput.getRaoInputs(), raoParameters); // if no inter-temporal constraints are defined, the results can be returned - if (raoInput.getGeneratorConstraints().isEmpty()) { + if (raoInput.getPowerGradients().isEmpty()) { OpenRaoLoggerProvider.TECHNICAL_LOGS.info("[MARMOT] No inter-temporal constraint provided; no need to re-optimize range actions"); return CompletableFuture.completedFuture(topologicalOptimizationResults); } @@ -135,7 +135,7 @@ private static LinearOptimizationResult optimizeLinearRemedialActions(InterTempo .withOutageInstant(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getCrac().getOutageInstant()) .withAppliedNetworkActionsInPrimaryState(preventiveTopologicalActions.getData(timestamp).orElseThrow()) .build())); - InterTemporalIteratingLinearOptimizerInput interTemporalLinearOptimizerInput = new InterTemporalIteratingLinearOptimizerInput(new TemporalDataImpl<>(linearOptimizerInputPerTimestamp), objectiveFunction, raoInput.getGeneratorConstraints()); + InterTemporalIteratingLinearOptimizerInput interTemporalLinearOptimizerInput = new InterTemporalIteratingLinearOptimizerInput(new TemporalDataImpl<>(linearOptimizerInputPerTimestamp), objectiveFunction, raoInput.getPowerGradients()); // Build parameters // Unoptimized cnec parameters ignored because only PRAs diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/PowerGradientConstraintFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/PowerGradientConstraintFillerTest.java index fa2daec114..170164c5fd 100644 --- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/PowerGradientConstraintFillerTest.java +++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/PowerGradientConstraintFillerTest.java @@ -17,7 +17,7 @@ import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeAction; import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction; import com.powsybl.openrao.data.crac.api.usagerule.UsageMethod; -import com.powsybl.openrao.data.generatorconstraints.GeneratorConstraints; +import com.powsybl.openrao.data.intertemporalconstraint.PowerGradient; import com.powsybl.openrao.data.raoresult.api.ComputationStatus; import com.powsybl.openrao.raoapi.InterTemporalRaoInput; import com.powsybl.openrao.raoapi.RaoInput; @@ -73,11 +73,12 @@ public void createThreeTSInput() throws IOException { RaoInput raoInput2 = RaoInput.build(network2, crac2).build(); RaoInput raoInput3 = RaoInput.build(network3, crac3).build(); - GeneratorConstraints generatorConstraintsFr1 = GeneratorConstraints.create().withGeneratorId("FFR1AA1 _load").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(500.0).withDownwardPowerGradient(-300.0).build(); - GeneratorConstraints generatorConstraintsFr2 = GeneratorConstraints.create().withGeneratorId("FFR2AA1 _generator").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(200.0).withDownwardPowerGradient(-100.0).build(); - GeneratorConstraints generatorConstraintsFr3 = GeneratorConstraints.create().withGeneratorId("FFR3AA1 _load").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(40.0).withDownwardPowerGradient(-150.0).build(); + //create powerGradientConstraint + PowerGradient powerGradientFFR1AA1 = new PowerGradient("FFR1AA1 _load", -300.0, 500.0); + PowerGradient powerGradientFFR2AA1 = new PowerGradient("FFR2AA1 _generator", -100.0, 200.0); + PowerGradient powerGradientFFR3AA1 = new PowerGradient("FFR3AA1 _load", -150.0, 40.0); - input = new InterTemporalRaoInput(new TemporalDataImpl<>(Map.of(timestamp1, raoInput1, timestamp2, raoInput2, timestamp3, raoInput3)), Set.of(generatorConstraintsFr1, generatorConstraintsFr2, generatorConstraintsFr3)); + input = new InterTemporalRaoInput(new TemporalDataImpl<>(Map.of(timestamp1, raoInput1, timestamp2, raoInput2, timestamp3, raoInput3)), Set.of(powerGradientFFR1AA1, powerGradientFFR3AA1, powerGradientFFR2AA1)); parameters = new RaoParameters(); } @@ -87,11 +88,12 @@ private void createOneTSInput() throws IOException { Crac crac1 = Crac.read("crac-1600.json", PowerGradientConstraintFillerTest.class.getResourceAsStream("/crac/crac-1600.json"), network1); RaoInput raoInput1 = RaoInput.build(network1, crac1).build(); - GeneratorConstraints generatorConstraintsFr1 = GeneratorConstraints.create().withGeneratorId("FFR1AA1 _load").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(500.0).withDownwardPowerGradient(-300.0).build(); - GeneratorConstraints generatorConstraintsFr2 = GeneratorConstraints.create().withGeneratorId("FFR2AA1 _generator").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(200.0).withDownwardPowerGradient(-100.0).build(); - GeneratorConstraints generatorConstraintsFr3 = GeneratorConstraints.create().withGeneratorId("FFR3AA1 _load").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(40.0).withDownwardPowerGradient(-150.0).build(); + //create powerGradientConstraint + PowerGradient powerGradientFFR1AA1 = new PowerGradient("FFR1AA1 _load", -300.0, 500.0); + PowerGradient powerGradientFFR2AA1 = new PowerGradient("FFR2AA1 _generator", -100.0, 200.0); + PowerGradient powerGradientFFR3AA1 = new PowerGradient("FFR3AA1 _load", -150.0, 40.0); - input = new InterTemporalRaoInput(new TemporalDataImpl<>(Map.of(timestamp1, raoInput1)), Set.of(generatorConstraintsFr1, generatorConstraintsFr2, generatorConstraintsFr3)); + input = new InterTemporalRaoInput(new TemporalDataImpl<>(Map.of(timestamp1, raoInput1)), Set.of(powerGradientFFR1AA1, powerGradientFFR2AA1, powerGradientFFR3AA1)); parameters = new RaoParameters(); } @@ -128,12 +130,12 @@ private void createPowerGradientConstraintFiller() { TemporalData preventiveStates = input.getRaoInputs().map(RaoInput::getCrac).map(crac -> crac.getPreventiveState()).map(State.class::cast); TemporalData networks = input.getRaoInputs().map(RaoInput::getNetwork).map(Network.class::cast); TemporalData> injectionRangeActions = input.getRaoInputs().map(RaoInput::getCrac).map(crac -> crac.getRangeActions(crac.getPreventiveState(), UsageMethod.AVAILABLE).stream().filter(InjectionRangeAction.class::isInstance).map(InjectionRangeAction.class::cast).collect(Collectors.toSet())); - Set generatorConstraints = input.getGeneratorConstraints(); + Set gradients = input.getPowerGradients(); PowerGradientConstraintFiller powerGradientConstraintFiller = new PowerGradientConstraintFiller( preventiveStates, networks, injectionRangeActions, - generatorConstraints); + gradients); linearProblemBuilder.withProblemFiller(powerGradientConstraintFiller); } @@ -236,11 +238,11 @@ void testGeneratorPowerGradientConstraintFiller() throws IOException { void testMissingGradientBound() throws IOException { createThreeTSInput(); - GeneratorConstraints generatorConstraintsFr1 = GeneratorConstraints.create().withGeneratorId("FFR1AA1 _load").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(960.0).build(); - GeneratorConstraints generatorConstraintsFr2 = GeneratorConstraints.create().withGeneratorId("FFR2AA1 _generator").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withDownwardPowerGradient(-100.0).build(); - GeneratorConstraints generatorConstraintsFr3 = GeneratorConstraints.create().withGeneratorId("FFR3AA1 _load").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(300.0).withDownwardPowerGradient(-200.0).build(); + PowerGradient powerGradientFFR1AA1 = new PowerGradient("FFR1AA1 _load", null, 960.0); + PowerGradient powerGradientFFR2AA1 = new PowerGradient("FFR2AA1 _generator", -100.0, null); + PowerGradient powerGradientFFR3AA1 = new PowerGradient("FFR3AA1 _load", -200.0, 300.0); - input = new InterTemporalRaoInput(new TemporalDataImpl<>(input.getRaoInputs().getDataPerTimestamp()), Set.of(generatorConstraintsFr1, generatorConstraintsFr2, generatorConstraintsFr3)); + input = new InterTemporalRaoInput(new TemporalDataImpl<>(input.getRaoInputs().getDataPerTimestamp()), Set.of(powerGradientFFR1AA1, powerGradientFFR3AA1, powerGradientFFR2AA1)); setUpLinearProblem(); diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/marmot/MarmotTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/marmot/MarmotTest.java index 1d2ce16af0..978935d917 100644 --- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/marmot/MarmotTest.java +++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/marmot/MarmotTest.java @@ -12,7 +12,7 @@ import com.powsybl.openrao.commons.TemporalDataImpl; import com.powsybl.openrao.data.crac.api.Crac; import com.powsybl.openrao.data.crac.api.InstantKind; -import com.powsybl.openrao.data.generatorconstraints.GeneratorConstraints; +import com.powsybl.openrao.data.intertemporalconstraint.PowerGradient; import com.powsybl.openrao.data.raoresult.api.RaoResult; import com.powsybl.openrao.raoapi.InterTemporalRaoInput; import com.powsybl.openrao.raoapi.RaoInput; @@ -47,7 +47,7 @@ void runCaseWithTwoTimestampsAndNoGradient() throws IOException { InterTemporalRaoInput input = new InterTemporalRaoInput( new TemporalDataImpl<>(Map.of(timestamp1, RaoInput.build(network1, crac1).build(), timestamp2, RaoInput.build(network2, crac2).build())), - Set.of(GeneratorConstraints.create().withGeneratorId("FFR1AA1 _generator").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(1000.0).withDownwardPowerGradient(-1000.0).build()) + Set.of(new PowerGradient("FFR1AA1 _generator", -1000d, 1000d)) ); // first RAOs shift tap to -5 for a cost of 55 each @@ -73,7 +73,7 @@ void testWithRedispatchingAndNoGradient() throws IOException { InterTemporalRaoInput input = new InterTemporalRaoInput( new TemporalDataImpl<>(Map.of(timestamp1, RaoInput.build(network1, crac1).build(), timestamp2, RaoInput.build(network2, crac2).build(), timestamp3, RaoInput.build(network3, crac3).build())), - Set.of(GeneratorConstraints.create().withGeneratorId("FFR1AA1 _generator").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(250.0).withDownwardPowerGradient(-250.0).build()) + Set.of(new PowerGradient("FFR1AA1 _generator", -250d, 250d)) ); // no redispatching required during the first timestamp @@ -102,7 +102,7 @@ void testWithRedispatchingAndGradient() throws IOException { InterTemporalRaoInput input = new InterTemporalRaoInput( new TemporalDataImpl<>(Map.of(timestamp1, RaoInput.build(network1, crac1).build(), timestamp2, RaoInput.build(network2, crac2).build(), timestamp3, RaoInput.build(network3, crac3).build())), - Set.of(GeneratorConstraints.create().withGeneratorId("FFR3AA1 _generator").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(200.0).withDownwardPowerGradient(0.0).build()) + Set.of(new PowerGradient("FFR3AA1 _generator", 0d, 200d)) ); // no redispatching required during the first timestamp @@ -129,7 +129,7 @@ void testWithPreventiveTopologicalAction() throws IOException { InterTemporalRaoInput input = new InterTemporalRaoInput( new TemporalDataImpl<>(Map.of(timestamp1, RaoInput.build(network1, crac1).build(), timestamp2, RaoInput.build(network2, crac2).build())), - Set.of(GeneratorConstraints.create().withGeneratorId("FFR1AA1 _generator").withLeadTime(0.0).withLagTime(0.0).withPMin(0.0).withPMax(1000.0).withUpwardPowerGradient(250.0).withDownwardPowerGradient(-250.0).build()) + Set.of(new PowerGradient("FFR1AA1 _generator", -250d, 250d)) ); TemporalData results = new Marmot().run(input, raoParameters).join(); From 6e466399ecc7ee079964c142efa27a55a8df264b Mon Sep 17 00:00:00 2001 From: Philippe Edwards Date: Tue, 11 Mar 2025 09:52:55 +0100 Subject: [PATCH 3/4] styling --- .../src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java index 3752719b9f..edef486508 100644 --- a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java +++ b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java @@ -137,4 +137,4 @@ private static boolean shouldBeImported(CSVRecord staticRecord) { return staticRecord.get("RD description mode").equals("NODE") && (staticRecord.get("Preventive").equals("TRUE") || staticRecord.get("Curative").equals("TRUE")); } -} \ No newline at end of file +} From 3e5816b4c888a2245a336289d77f77ffc7fd3441 Mon Sep 17 00:00:00 2001 From: Philippe Edwards Date: Mon, 17 Mar 2025 11:06:57 +0100 Subject: [PATCH 4/4] added check on gradient constraint and only import preventive actions Signed-off-by: Philippe Edwards --- .../powsybl/openrao/raoapi/IcsImporter.java | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java index edef486508..72fbe5f605 100644 --- a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java +++ b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/IcsImporter.java @@ -15,12 +15,15 @@ import java.io.InputStreamReader; import java.time.OffsetDateTime; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; +import java.util.Set; public final class IcsImporter { private static final int OFFSET = 2; private static final double COST_UP = 10; private static final double COST_DOWN = 10; + private static final double ACTIVATION_COST = 50; //TODO:QUALITY CHECK: do PO respect constraints? @@ -47,14 +50,14 @@ public static void populateInputWithICS(InterTemporalRaoInput interTemporalRaoIn if (shouldBeImported(staticRecord)) { String raId = staticRecord.get("RA RD ID"); Map seriesPerType = seriesPerIdAndType.get(raId); - if (seriesPerType != null && seriesPerType.containsKey("P0") && seriesPerType.containsKey("RDP-") && seriesPerType.containsKey("RDP+")) { + if (seriesPerType != null && seriesPerType.containsKey("P0") && seriesPerType.containsKey("RDP-") && seriesPerType.containsKey("RDP+") && P0RespectsGradients(staticRecord, seriesPerType.get("P0"), interTemporalRaoInput.getTimestampsToRun())) { String networkElement = processNetworks(staticRecord.get("UCT Node or GSK ID"), interTemporalRaoInput, seriesPerType); if (networkElement == null) { return; } interTemporalRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { Crac crac = raoInput.getCrac(); - Double p0 = Double.parseDouble(seriesPerType.get("P0").get(dateTime.getHour() + OFFSET)); + double p0 = Double.parseDouble(seriesPerType.get("P0").get(dateTime.getHour() + OFFSET)); InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() .withId(raId + "_RD") .withName(staticRecord.get("Generator Name")) @@ -62,6 +65,7 @@ public static void populateInputWithICS(InterTemporalRaoInput interTemporalRaoIn .withInitialSetpoint(p0) .withVariationCost(COST_UP, VariationDirection.UP) .withVariationCost(COST_DOWN, VariationDirection.DOWN) + //.withActivationCost(ACTIVATION_COST) .newRange() .withMin(p0 - Double.parseDouble(seriesPerType.get("RDP-").get(dateTime.getHour() + OFFSET))) .withMax(p0 + Double.parseDouble(seriesPerType.get("RDP+").get(dateTime.getHour() + OFFSET))) @@ -72,12 +76,12 @@ public static void populateInputWithICS(InterTemporalRaoInput interTemporalRaoIn .withUsageMethod(UsageMethod.AVAILABLE) .add(); } - if (staticRecord.get("Curative").equals("TRUE")) { + /*if (staticRecord.get("Curative").equals("TRUE")) { injectionRangeActionAdder.newOnInstantUsageRule() .withInstant(crac.getLastInstant().getId()) .withUsageMethod(UsageMethod.AVAILABLE) .add(); - } + }*/ injectionRangeActionAdder.add(); }); @@ -135,6 +139,25 @@ private static void processBus(Bus bus, String generatorId, Double p0) { private static boolean shouldBeImported(CSVRecord staticRecord) { return staticRecord.get("RD description mode").equals("NODE") && - (staticRecord.get("Preventive").equals("TRUE") || staticRecord.get("Curative").equals("TRUE")); + (staticRecord.get("Preventive").equals("TRUE")/* || staticRecord.get("Curative").equals("TRUE")*/); + } + private static boolean P0RespectsGradients(CSVRecord staticRecord, CSVRecord P0record, Set dateTimes) { + double maxGradient = Double.parseDouble(staticRecord.get("Maximum positive power gradient [MW/h]").isEmpty() ? + "1000" : staticRecord.get("Maximum positive power gradient [MW/h]")); + double minGradient = -Double.parseDouble(staticRecord.get("Maximum negative power gradient [MW/h]").isEmpty() ? + "1000" : staticRecord.get("Maximum negative power gradient [MW/h]")); + + Iterator dateTimeIterator = dateTimes.iterator(); + OffsetDateTime currentDateTime = dateTimeIterator.next(); + while(dateTimeIterator.hasNext()) { + OffsetDateTime nextDateTime = dateTimeIterator.next(); + double diff = Double.parseDouble(P0record.get(nextDateTime.getHour() + OFFSET)) - Double.parseDouble(P0record.get(currentDateTime.getHour() + OFFSET)); + if (diff > maxGradient || diff < minGradient) { + System.out.printf("%s does not respect power gradients : min/max/diff %f %f %f%n", staticRecord.get(0), minGradient, maxGradient, diff); + return false; + } + currentDateTime = nextDateTime; + } + return true; } }